From 48adcf148c83faa41999fb0b3524299c4e160fd9 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 01:03:16 +0300 Subject: [CPUFREQ] cpufreq: remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: Dave Jones --- include/linux/cpufreq.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index e7e91dbfde0f..2270ca5ec631 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -4,9 +4,6 @@ * Copyright (C) 2001 Russell King * (C) 2002 - 2003 Dominik Brodowski * - * - * $Id: cpufreq.h,v 1.36 2003/01/20 17:31:48 db Exp $ - * * 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. -- cgit From 41df0d61c266998b8049df7fec119cd518a43aa1 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Mon, 12 May 2008 21:21:13 +0200 Subject: x86: Add performance variants of cpumask operators * Increase performance for systems with large count NR_CPUS by limiting the range of the cpumask operators that loop over the bits in a cpumask_t variable. This removes a large amount of wasted cpu cycles. * Add performance variants of the cpumask operators: int cpus_weight_nr(mask) Same using nr_cpu_ids instead of NR_CPUS int first_cpu_nr(mask) Number lowest set bit, or nr_cpu_ids int next_cpu_nr(cpu, mask) Next cpu past 'cpu', or nr_cpu_ids for_each_cpu_mask_nr(cpu, mask) for-loop cpu over mask using nr_cpu_ids * Modify following to use performance variants: #define num_online_cpus() cpus_weight_nr(cpu_online_map) #define num_possible_cpus() cpus_weight_nr(cpu_possible_map) #define num_present_cpus() cpus_weight_nr(cpu_present_map) #define for_each_possible_cpu(cpu) for_each_cpu_mask_nr((cpu), ...) #define for_each_online_cpu(cpu) for_each_cpu_mask_nr((cpu), ...) #define for_each_present_cpu(cpu) for_each_cpu_mask_nr((cpu), ...) * Comment added to include/linux/cpumask.h: Note: The alternate operations with the suffix "_nr" are used to limit the range of the loop to nr_cpu_ids instead of NR_CPUS when NR_CPUS > 64 for performance reasons. If NR_CPUS is <= 64 then most assembler bitmask operators execute faster with a constant range, so the operator will continue to use NR_CPUS. Another consideration is that nr_cpu_ids is initialized to NR_CPUS and isn't lowered until the possible cpus are discovered (including any disabled cpus). So early uses will span the entire range of NR_CPUS. (The net effect is that for systems with 64 or less CPU's there are no functional changes.) For inclusion into sched-devel/latest tree. Based on: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git + sched-devel/latest .../mingo/linux-2.6-sched-devel.git Cc: Paul Jackson Cc: Christoph Lameter Reviewed-by: Paul Jackson Reviewed-by: Christoph Lameter Signed-off-by: Mike Travis Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 92 +++++++++++++++++++++++++++++++++---------------- lib/cpumask.c | 9 +++++ 2 files changed, 71 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 5df3db58fcc6..b49472d1af84 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -17,6 +17,20 @@ * For details of cpus_onto(), see bitmap_onto in lib/bitmap.c. * For details of cpus_fold(), see bitmap_fold in lib/bitmap.c. * + * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + * Note: The alternate operations with the suffix "_nr" are used + * to limit the range of the loop to nr_cpu_ids instead of + * NR_CPUS when NR_CPUS > 64 for performance reasons. + * If NR_CPUS is <= 64 then most assembler bitmask + * operators execute faster with a constant range, so + * the operator will continue to use NR_CPUS. + * + * Another consideration is that nr_cpu_ids is initialized + * to NR_CPUS and isn't lowered until the possible cpus are + * discovered (including any disabled cpus). So early uses + * will span the entire range of NR_CPUS. + * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + * * The available cpumask operations are: * * void cpu_set(cpu, mask) turn on bit 'cpu' in mask @@ -38,12 +52,14 @@ * int cpus_empty(mask) Is mask empty (no bits sets)? * int cpus_full(mask) Is mask full (all bits sets)? * int cpus_weight(mask) Hamming weigh - number of set bits + * int cpus_weight_nr(mask) Same using nr_cpu_ids instead of NR_CPUS * * void cpus_shift_right(dst, src, n) Shift right * void cpus_shift_left(dst, src, n) Shift left * * int first_cpu(mask) Number lowest set bit, or NR_CPUS * int next_cpu(cpu, mask) Next cpu past 'cpu', or NR_CPUS + * int next_cpu_nr(cpu, mask) Next cpu past 'cpu', or nr_cpu_ids * * cpumask_t cpumask_of_cpu(cpu) Return cpumask with bit 'cpu' set * CPU_MASK_ALL Initializer - all bits set @@ -59,7 +75,8 @@ * void cpus_onto(dst, orig, relmap) *dst = orig relative to relmap * void cpus_fold(dst, orig, sz) dst bits = orig bits mod sz * - * for_each_cpu_mask(cpu, mask) for-loop cpu over mask + * for_each_cpu_mask(cpu, mask) for-loop cpu over mask using NR_CPUS + * for_each_cpu_mask_nr(cpu, mask) for-loop cpu over mask using nr_cpu_ids * * int num_online_cpus() Number of online CPUs * int num_possible_cpus() Number of all possible CPUs @@ -216,15 +233,6 @@ static inline void __cpus_shift_left(cpumask_t *dstp, bitmap_shift_left(dstp->bits, srcp->bits, n, nbits); } -#ifdef CONFIG_SMP -int __first_cpu(const cpumask_t *srcp); -#define first_cpu(src) __first_cpu(&(src)) -int __next_cpu(int n, const cpumask_t *srcp); -#define next_cpu(n, src) __next_cpu((n), &(src)) -#else -#define first_cpu(src) ({ (void)(src); 0; }) -#define next_cpu(n, src) ({ (void)(src); 1; }) -#endif #ifdef CONFIG_HAVE_CPUMASK_OF_CPU_MAP extern cpumask_t *cpumask_of_cpu_map; @@ -343,15 +351,48 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp, bitmap_fold(dstp->bits, origp->bits, sz, nbits); } -#if NR_CPUS > 1 +#if NR_CPUS == 1 + +#define nr_cpu_ids 1 +#define first_cpu(src) ({ (void)(src); 0; }) +#define next_cpu(n, src) ({ (void)(src); 1; }) +#define any_online_cpu(mask) 0 +#define for_each_cpu_mask(cpu, mask) \ + for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask) + +#else /* NR_CPUS > 1 */ + +extern int nr_cpu_ids; +int __first_cpu(const cpumask_t *srcp); +int __next_cpu(int n, const cpumask_t *srcp); +int __any_online_cpu(const cpumask_t *mask); + +#define first_cpu(src) __first_cpu(&(src)) +#define next_cpu(n, src) __next_cpu((n), &(src)) +#define any_online_cpu(mask) __any_online_cpu(&(mask)) #define for_each_cpu_mask(cpu, mask) \ for ((cpu) = first_cpu(mask); \ (cpu) < NR_CPUS; \ (cpu) = next_cpu((cpu), (mask))) -#else /* NR_CPUS == 1 */ -#define for_each_cpu_mask(cpu, mask) \ - for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask) -#endif /* NR_CPUS */ +#endif + +#if NR_CPUS <= 64 + +#define next_cpu_nr(n, src) next_cpu(n, src) +#define cpus_weight_nr(cpumask) cpus_weight(cpumask) +#define for_each_cpu_mask_nr(cpu, mask) for_each_cpu_mask(cpu, mask) + +#else /* NR_CPUS > 64 */ + +int __next_cpu_nr(int n, const cpumask_t *srcp); +#define next_cpu_nr(n, src) __next_cpu_nr((n), &(src)) +#define cpus_weight_nr(cpumask) __cpus_weight(&(cpumask), nr_cpu_ids) +#define for_each_cpu_mask_nr(cpu, mask) \ + for ((cpu) = first_cpu(mask); \ + (cpu) < nr_cpu_ids; \ + (cpu) = next_cpu_nr((cpu), (mask))) + +#endif /* NR_CPUS > 64 */ /* * The following particular system cpumasks and operations manage @@ -414,9 +455,9 @@ extern cpumask_t cpu_online_map; extern cpumask_t cpu_present_map; #if NR_CPUS > 1 -#define num_online_cpus() cpus_weight(cpu_online_map) -#define num_possible_cpus() cpus_weight(cpu_possible_map) -#define num_present_cpus() cpus_weight(cpu_present_map) +#define num_online_cpus() cpus_weight_nr(cpu_online_map) +#define num_possible_cpus() cpus_weight_nr(cpu_possible_map) +#define num_present_cpus() cpus_weight_nr(cpu_present_map) #define cpu_online(cpu) cpu_isset((cpu), cpu_online_map) #define cpu_possible(cpu) cpu_isset((cpu), cpu_possible_map) #define cpu_present(cpu) cpu_isset((cpu), cpu_present_map) @@ -431,17 +472,8 @@ extern cpumask_t cpu_present_map; #define cpu_is_offline(cpu) unlikely(!cpu_online(cpu)) -#ifdef CONFIG_SMP -extern int nr_cpu_ids; -#define any_online_cpu(mask) __any_online_cpu(&(mask)) -int __any_online_cpu(const cpumask_t *mask); -#else -#define nr_cpu_ids 1 -#define any_online_cpu(mask) 0 -#endif - -#define for_each_possible_cpu(cpu) for_each_cpu_mask((cpu), cpu_possible_map) -#define for_each_online_cpu(cpu) for_each_cpu_mask((cpu), cpu_online_map) -#define for_each_present_cpu(cpu) for_each_cpu_mask((cpu), cpu_present_map) +#define for_each_possible_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_possible_map) +#define for_each_online_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_online_map) +#define for_each_present_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_present_map) #endif /* __LINUX_CPUMASK_H */ diff --git a/lib/cpumask.c b/lib/cpumask.c index bb4f76d3c3e7..5f97dc25ef9c 100644 --- a/lib/cpumask.c +++ b/lib/cpumask.c @@ -15,6 +15,15 @@ int __next_cpu(int n, const cpumask_t *srcp) } EXPORT_SYMBOL(__next_cpu); +#if NR_CPUS > 64 +int __next_cpu_nr(int n, const cpumask_t *srcp) +{ + return min_t(int, nr_cpu_ids, + find_next_bit(srcp->bits, nr_cpu_ids, n+1)); +} +EXPORT_SYMBOL(__next_cpu_nr); +#endif + int __any_online_cpu(const cpumask_t *mask) { int cpu; -- cgit From 7baac8b91f9871ba8cb09af84de4ae1d86d07812 Mon Sep 17 00:00:00 2001 From: Alexander van Heukelum Date: Tue, 13 May 2008 11:28:21 +0200 Subject: cpumask: make for_each_cpu_mask a bit smaller The for_each_cpu_mask loop is used quite often in the kernel. It makes use of two functions: first_cpu and next_cpu. This patch changes for_each_cpu_mask to use only the latter. Because next_cpu finds the next eligible cpu _after_ the given one, the iteration variable has to be initialized to -1 and next_cpu has to be called with this value before the first iteration. An x86_64 defconfig kernel (from sched/latest) is about 2500 bytes smaller with this patch applied: text data bss dec hex filename 6222517 917952 749932 7890401 7865e1 vmlinux.orig 6219922 917952 749932 7887806 785bbe vmlinux The same size reduction is seen for defconfig+MAXSMP text data bss dec hex filename 6241772 2563968 1492716 10298456 9d2458 vmlinux.orig 6239211 2563968 1492716 10295895 9d1a57 vmlinux Signed-off-by: Alexander van Heukelum Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/cpumask.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index b49472d1af84..80226e776143 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -370,10 +370,10 @@ int __any_online_cpu(const cpumask_t *mask); #define first_cpu(src) __first_cpu(&(src)) #define next_cpu(n, src) __next_cpu((n), &(src)) #define any_online_cpu(mask) __any_online_cpu(&(mask)) -#define for_each_cpu_mask(cpu, mask) \ - for ((cpu) = first_cpu(mask); \ - (cpu) < NR_CPUS; \ - (cpu) = next_cpu((cpu), (mask))) +#define for_each_cpu_mask(cpu, mask) \ + for ((cpu) = -1; \ + (cpu) = next_cpu((cpu), (mask)), \ + (cpu) < NR_CPUS; ) #endif #if NR_CPUS <= 64 @@ -387,10 +387,10 @@ int __any_online_cpu(const cpumask_t *mask); int __next_cpu_nr(int n, const cpumask_t *srcp); #define next_cpu_nr(n, src) __next_cpu_nr((n), &(src)) #define cpus_weight_nr(cpumask) __cpus_weight(&(cpumask), nr_cpu_ids) -#define for_each_cpu_mask_nr(cpu, mask) \ - for ((cpu) = first_cpu(mask); \ - (cpu) < nr_cpu_ids; \ - (cpu) = next_cpu_nr((cpu), (mask))) +#define for_each_cpu_mask_nr(cpu, mask) \ + for ((cpu) = -1; \ + (cpu) = next_cpu_nr((cpu), (mask)), \ + (cpu) < nr_cpu_ids; ) #endif /* NR_CPUS > 64 */ -- cgit From 9c44bc03fff44ff04237a7d92e35304a0e50c331 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 12 May 2008 21:21:04 +0200 Subject: softlockup: allow panic on lockup allow users to configure the softlockup detector to generate a panic instead of a warning message. high-availability systems might opt for this strict method (combined with panic_timeout= boot option/sysctl), instead of generating softlockup warnings ad infinitum. also, automated tests work better if the system reboots reliably (into a safe kernel) in case of a lockup. The full spectrum of configurability is supported: boot option, sysctl option and Kconfig option. it's default-disabled. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- Documentation/kernel-parameters.txt | 3 +++ include/linux/sched.h | 3 ++- kernel/softlockup.c | 21 +++++++++++++++++++++ kernel/sysctl.c | 11 +++++++++++ lib/Kconfig.debug | 26 +++++++++++++++++++++++++- 5 files changed, 62 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e07c432c731f..042588fa12e5 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1971,6 +1971,9 @@ and is between 256 and 4096 characters. It is defined in the file snd-ymfpci= [HW,ALSA] + softlockup_panic= + [KNL] Should the soft-lockup detector generate panics. + sonypi.*= [HW] Sony Programmable I/O Control Device driver See Documentation/sonypi.txt diff --git a/include/linux/sched.h b/include/linux/sched.h index 5395a6176f4b..71f5972dc48e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -294,7 +294,8 @@ extern void softlockup_tick(void); extern void spawn_softlockup_task(void); extern void touch_softlockup_watchdog(void); extern void touch_all_softlockup_watchdogs(void); -extern unsigned long softlockup_thresh; +extern unsigned int softlockup_panic; +extern unsigned long softlockup_thresh; extern unsigned long sysctl_hung_task_check_count; extern unsigned long sysctl_hung_task_timeout_secs; extern unsigned long sysctl_hung_task_warnings; diff --git a/kernel/softlockup.c b/kernel/softlockup.c index 01b6522fd92b..78e0ad21cb0c 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -27,6 +27,21 @@ static DEFINE_PER_CPU(struct task_struct *, watchdog_task); static int __read_mostly did_panic; unsigned long __read_mostly softlockup_thresh = 60; +/* + * Should we panic (and reboot, if panic_timeout= is set) when a + * soft-lockup occurs: + */ +unsigned int __read_mostly softlockup_panic = + CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE; + +static int __init softlockup_panic_setup(char *str) +{ + softlockup_panic = simple_strtoul(str, NULL, 0); + + return 1; +} +__setup("softlockup_panic=", softlockup_panic_setup); + static int softlock_panic(struct notifier_block *this, unsigned long event, void *ptr) { @@ -120,6 +135,9 @@ void softlockup_tick(void) else dump_stack(); spin_unlock(&print_lock); + + if (softlockup_panic) + panic("softlockup: hung tasks"); } /* @@ -172,6 +190,9 @@ static void check_hung_task(struct task_struct *t, unsigned long now) t->last_switch_timestamp = now; touch_nmi_watchdog(); + + if (softlockup_panic) + panic("softlockup: blocked tasks"); } /* diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 29116652dca8..2d3b388c402d 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -727,6 +727,17 @@ static struct ctl_table kern_table[] = { }, #endif #ifdef CONFIG_DETECT_SOFTLOCKUP + { + .ctl_name = CTL_UNNUMBERED, + .procname = "softlockup_panic", + .data = &softlockup_panic, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_doulongvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &zero, + .extra2 = &one, + }, { .ctl_name = CTL_UNNUMBERED, .procname = "softlockup_thresh", diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d2099f41aa1e..509ae35a9ef5 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -147,7 +147,7 @@ config DETECT_SOFTLOCKUP help Say Y here to enable the kernel to detect "soft lockups", which are bugs that cause the kernel to loop in kernel - mode for more than 10 seconds, without giving other tasks a + mode for more than 60 seconds, without giving other tasks a chance to run. When a soft-lockup is detected, the kernel will print the @@ -159,6 +159,30 @@ config DETECT_SOFTLOCKUP can be detected via the NMI-watchdog, on platforms that support it.) +config BOOTPARAM_SOFTLOCKUP_PANIC + bool "Panic (Reboot) On Soft Lockups" + depends on DETECT_SOFTLOCKUP + help + Say Y here to enable the kernel to panic on "soft lockups", + which are bugs that cause the kernel to loop in kernel + mode for more than 60 seconds, without giving other tasks a + chance to run. + + The panic can be used in combination with panic_timeout, + to cause the system to reboot automatically after a + lockup has been detected. This feature is useful for + high-availability systems that have uptime guarantees and + where a lockup must be resolved ASAP. + + Say N if unsure. + +config BOOTPARAM_SOFTLOCKUP_PANIC_VALUE + int + depends on DETECT_SOFTLOCKUP + range 0 1 + default 0 if !BOOTPARAM_SOFTLOCKUP_PANIC + default 1 if BOOTPARAM_SOFTLOCKUP_PANIC + config SCHED_DEBUG bool "Collect scheduler debugging info" depends on DEBUG_KERNEL && PROC_FS -- cgit From 9383d9679056e6cc4e7ff70f31da945a268238f4 Mon Sep 17 00:00:00 2001 From: Dimitri Sivanich Date: Mon, 12 May 2008 21:21:14 +0200 Subject: softlockup: fix softlockup_thresh unaligned access and disable detection at runtime Fix unaligned access errors when setting softlockup_thresh on 64 bit platforms. Allow softlockup detection to be disabled by setting softlockup_thresh <= 0. Detect that boot time softlockup detection has been disabled earlier in softlockup_tick. Signed-off-by: Dimitri Sivanich Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 2 +- kernel/softlockup.c | 12 ++++++++++-- kernel/sysctl.c | 9 +++++---- 3 files changed, 16 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 71f5972dc48e..ea26221644e2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -295,10 +295,10 @@ extern void spawn_softlockup_task(void); extern void touch_softlockup_watchdog(void); extern void touch_all_softlockup_watchdogs(void); extern unsigned int softlockup_panic; -extern unsigned long softlockup_thresh; extern unsigned long sysctl_hung_task_check_count; extern unsigned long sysctl_hung_task_timeout_secs; extern unsigned long sysctl_hung_task_warnings; +extern int softlockup_thresh; #else static inline void softlockup_tick(void) { diff --git a/kernel/softlockup.c b/kernel/softlockup.c index 78e0ad21cb0c..a3a0b239b7f7 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -25,7 +25,7 @@ static DEFINE_PER_CPU(unsigned long, print_timestamp); static DEFINE_PER_CPU(struct task_struct *, watchdog_task); static int __read_mostly did_panic; -unsigned long __read_mostly softlockup_thresh = 60; +int __read_mostly softlockup_thresh = 60; /* * Should we panic (and reboot, if panic_timeout= is set) when a @@ -94,6 +94,14 @@ void softlockup_tick(void) struct pt_regs *regs = get_irq_regs(); unsigned long now; + /* Is detection switched off? */ + if (!per_cpu(watchdog_task, this_cpu) || softlockup_thresh <= 0) { + /* Be sure we don't false trigger if switched back on */ + if (touch_timestamp) + per_cpu(touch_timestamp, this_cpu) = 0; + return; + } + if (touch_timestamp == 0) { touch_softlockup_watchdog(); return; @@ -104,7 +112,7 @@ void softlockup_tick(void) /* report at most once a second */ if ((print_timestamp >= touch_timestamp && print_timestamp < (touch_timestamp + 1)) || - did_panic || !per_cpu(watchdog_task, this_cpu)) { + did_panic) { return; } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 2d3b388c402d..31c19a79738d 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -84,12 +84,13 @@ extern int latencytop_enabled; extern int sysctl_nr_open_min, sysctl_nr_open_max; /* Constants used for minimum and maximum */ -#if defined(CONFIG_DETECT_SOFTLOCKUP) || defined(CONFIG_HIGHMEM) +#ifdef CONFIG_HIGHMEM static int one = 1; #endif #ifdef CONFIG_DETECT_SOFTLOCKUP static int sixty = 60; +static int neg_one = -1; #endif #ifdef CONFIG_MMU @@ -742,11 +743,11 @@ static struct ctl_table kern_table[] = { .ctl_name = CTL_UNNUMBERED, .procname = "softlockup_thresh", .data = &softlockup_thresh, - .maxlen = sizeof(unsigned long), + .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_doulongvec_minmax, + .proc_handler = &proc_dointvec_minmax, .strategy = &sysctl_intvec, - .extra1 = &one, + .extra1 = &neg_one, .extra2 = &sixty, }, { -- cgit From a29ccf6f823a84d89e1c7aaaf221cf7282022024 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 3 Jun 2008 14:59:40 +0100 Subject: Make console charset translation optional By turning off the new CONSOLE_TRANSLATIONS option and dropping the associated code and tables from the kernel, we can save about 7KiB. Taken from linux-tiny project by Tim Bird and mangled further by dwmw2. Signed-off-by: Tim Bird Signed-off-by: David Woodhouse --- drivers/char/Kconfig | 8 ++++++++ drivers/char/Makefile | 4 ++-- drivers/char/vt.c | 2 +- include/linux/consolemap.h | 14 ++++++++++++++ include/linux/vt_kern.h | 19 +++++++++++++++++++ 5 files changed, 44 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 595a925c62a9..b7f7371dee73 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -36,6 +36,14 @@ config VT If unsure, say Y, or else you won't be able to do much with your new shiny Linux system :-) +config CONSOLE_TRANSLATIONS + depends on VT + default y + bool "Enable character translations in console" if EMBEDDED + ---help--- + This enables support for font mapping and Unicode translation + on virtual consoles. + config VT_CONSOLE bool "Support for console on virtual terminal" if EMBEDDED depends on VT diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 4c1c584e9eb6..6ef173cab144 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -12,8 +12,8 @@ obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o obj-y += misc.o -obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o \ - consolemap_deftbl.o selection.o keyboard.o +obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o +obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o obj-$(CONFIG_AUDIT) += tty_audit.o obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o diff --git a/drivers/char/vt.c b/drivers/char/vt.c index fa1ffbf2c621..18b7fb06dace 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2208,7 +2208,7 @@ rescan_last_byte: c = 0xfffd; tc = c; } else { /* no utf or alternate charset mode */ - tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c]; + tc = vc_translate(vc, c); } param.c = tc; diff --git a/include/linux/consolemap.h b/include/linux/consolemap.h index e2bf7e5db39a..c4811da1338b 100644 --- a/include/linux/consolemap.h +++ b/include/linux/consolemap.h @@ -3,6 +3,9 @@ * * Interface between console.c, selection.c and consolemap.c */ +#ifndef __LINUX_CONSOLEMAP_H__ +#define __LINUX_CONSOLEMAP_H__ + #define LAT1_MAP 0 #define GRAF_MAP 1 #define IBMPC_MAP 2 @@ -10,6 +13,7 @@ #include +#ifdef CONFIG_CONSOLE_TRANSLATIONS struct vc_data; extern u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode); @@ -18,3 +22,13 @@ extern int conv_uni_to_pc(struct vc_data *conp, long ucs); extern u32 conv_8bit_to_uni(unsigned char c); extern int conv_uni_to_8bit(u32 uni); void console_map_init(void); +#else +#define inverse_translate(conp, glyph, uni) ((uint16_t)glyph) +#define set_translate(m, vc) ((unsigned short *)NULL) +#define conv_uni_to_pc(conp, ucs) ((int) (ucs > 0xff ? -1: ucs)) +#define conv_8bit_to_uni(c) ((uint32_t)(c)) +#define conv_uni_to_8bit(c) ((int) ((c) & 0xff)) +#define console_map_init(c) do { ; } while (0) +#endif /* CONFIG_CONSOLE_TRANSLATIONS */ + +#endif /* __LINUX_CONSOLEMAP_H__ */ diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 9448ffbdcbf6..14c0e91be9b5 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -12,6 +12,7 @@ #include #include #include +#include /* * Presently, a lot of graphics programs do not restore the contents of @@ -54,6 +55,7 @@ void redraw_screen(struct vc_data *vc, int is_switch); struct tty_struct; int tioclinux(struct tty_struct *tty, unsigned long arg); +#ifdef CONFIG_CONSOLE_TRANSLATIONS /* consolemap.c */ struct unimapinit; @@ -71,6 +73,23 @@ void con_free_unimap(struct vc_data *vc); void con_protect_unimap(struct vc_data *vc, int rdonly); int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc); +#define vc_translate(vc, c) ((vc)->vc_translate[(c) | \ + (vc)->vc_toggle_meta ? 0x80 : 0]) +#else +#define con_set_trans_old(arg) (0) +#define con_get_trans_old(arg) (-EINVAL) +#define con_set_trans_new(arg) (0) +#define con_get_trans_new(arg) (-EINVAL) +#define con_clear_unimap(vc, ui) (0) +#define con_set_unimap(vc, ct, list) (0) +#define con_set_default_unimap(vc) (0) +#define con_copy_unimap(d, s) (0) +#define con_get_unimap(vc, ct, uct, list) (-EINVAL) +#define con_free_unimap(vc) do { ; } while (0) + +#define vc_translate(vc, c) (c) +#endif + /* vt.c */ int vt_waitactive(int vt); void change_console(struct vc_data *new_vc); -- cgit From 59018b6d2acabb114ab58637e6ab95ba424a89d0 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 01:03:52 +0300 Subject: MTD/JFFS2: remove CVS keywords Once upon a time, the MTD repository was using CVS. This patch therefore removes all usages of the no longer updated CVS keywords from the MTD code. This also includes code that printed them to the user. Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 2 -- drivers/mtd/afs.c | 2 -- drivers/mtd/chips/cfi_cmdset_0001.c | 2 -- drivers/mtd/chips/cfi_cmdset_0002.c | 3 --- drivers/mtd/chips/cfi_cmdset_0020.c | 2 -- drivers/mtd/chips/cfi_probe.c | 1 - drivers/mtd/chips/cfi_util.c | 3 --- drivers/mtd/chips/chipreg.c | 2 -- drivers/mtd/chips/gen_probe.c | 1 - drivers/mtd/chips/jedec_probe.c | 1 - drivers/mtd/chips/map_absent.c | 1 - drivers/mtd/chips/map_ram.c | 1 - drivers/mtd/chips/map_rom.c | 1 - drivers/mtd/cmdlinepart.c | 2 -- drivers/mtd/devices/Kconfig | 1 - drivers/mtd/devices/Makefile | 1 - drivers/mtd/devices/block2mtd.c | 6 ------ drivers/mtd/devices/doc2000.c | 2 -- drivers/mtd/devices/doc2001.c | 2 -- drivers/mtd/devices/doc2001plus.c | 2 -- drivers/mtd/devices/docecc.c | 2 -- drivers/mtd/devices/docprobe.c | 3 --- drivers/mtd/devices/lart.c | 2 -- drivers/mtd/devices/ms02-nv.c | 2 -- drivers/mtd/devices/ms02-nv.h | 2 -- drivers/mtd/devices/mtdram.c | 1 - drivers/mtd/devices/phram.c | 2 -- drivers/mtd/devices/pmc551.c | 2 -- drivers/mtd/devices/slram.c | 2 -- drivers/mtd/ftl.c | 3 --- drivers/mtd/inftlcore.c | 5 ----- drivers/mtd/inftlmount.c | 4 ---- drivers/mtd/maps/Kconfig | 1 - drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/amd76xrom.c | 1 - drivers/mtd/maps/autcpu12-nvram.c | 2 -- drivers/mtd/maps/bast-flash.c | 2 -- drivers/mtd/maps/cdb89712.c | 1 - drivers/mtd/maps/ceiva.c | 1 - drivers/mtd/maps/cfi_flagadm.c | 2 -- drivers/mtd/maps/dbox2-flash.c | 2 -- drivers/mtd/maps/dc21285.c | 2 -- drivers/mtd/maps/dilnetpc.c | 2 -- drivers/mtd/maps/dmv182.c | 2 -- drivers/mtd/maps/ebony.c | 2 -- drivers/mtd/maps/edb7312.c | 2 -- drivers/mtd/maps/fortunet.c | 1 - drivers/mtd/maps/h720x-flash.c | 2 -- drivers/mtd/maps/ichxrom.c | 1 - drivers/mtd/maps/impa7.c | 2 -- drivers/mtd/maps/integrator-flash.c | 2 -- drivers/mtd/maps/ipaq-flash.c | 2 -- drivers/mtd/maps/ixp2000.c | 2 -- drivers/mtd/maps/ixp4xx.c | 2 -- drivers/mtd/maps/l440gx.c | 2 -- drivers/mtd/maps/map_funcs.c | 2 -- drivers/mtd/maps/mbx860.c | 2 -- drivers/mtd/maps/mtx-1_flash.c | 2 -- drivers/mtd/maps/netsc520.c | 2 -- drivers/mtd/maps/nettel.c | 2 -- drivers/mtd/maps/octagon-5066.c | 1 - drivers/mtd/maps/omap-toto-flash.c | 2 -- drivers/mtd/maps/pci.c | 2 -- drivers/mtd/maps/pcmciamtd.c | 5 +---- drivers/mtd/maps/physmap.c | 2 -- drivers/mtd/maps/plat-ram.c | 2 -- drivers/mtd/maps/redwood.c | 2 -- drivers/mtd/maps/rpxlite.c | 2 -- drivers/mtd/maps/sa1100-flash.c | 2 -- drivers/mtd/maps/sbc8240.c | 3 --- drivers/mtd/maps/sbc_gxx.c | 2 -- drivers/mtd/maps/sc520cdp.c | 2 -- drivers/mtd/maps/scb2_flash.c | 1 - drivers/mtd/maps/scx200_docflash.c | 2 -- drivers/mtd/maps/sharpsl-flash.c | 2 -- drivers/mtd/maps/solutionengine.c | 2 -- drivers/mtd/maps/sun_uflash.c | 2 +- drivers/mtd/maps/tqm8xxl.c | 2 -- drivers/mtd/maps/ts5500_flash.c | 2 -- drivers/mtd/maps/tsunami_flash.c | 1 - drivers/mtd/maps/uclinux.c | 2 -- drivers/mtd/maps/vmax301.c | 1 - drivers/mtd/maps/walnut.c | 2 -- drivers/mtd/maps/wr_sbc82xx_flash.c | 2 -- drivers/mtd/mtd_blkdevs.c | 2 -- drivers/mtd/mtdblock.c | 2 -- drivers/mtd/mtdblock_ro.c | 2 -- drivers/mtd/mtdchar.c | 2 -- drivers/mtd/mtdconcat.c | 2 -- drivers/mtd/mtdcore.c | 2 -- drivers/mtd/mtdpart.c | 2 -- drivers/mtd/nand/Kconfig | 1 - drivers/mtd/nand/Makefile | 1 - drivers/mtd/nand/au1550nd.c | 2 -- drivers/mtd/nand/autcpu12.c | 2 -- drivers/mtd/nand/diskonchip.c | 2 -- drivers/mtd/nand/edb7312.c | 2 -- drivers/mtd/nand/h1910.c | 2 -- drivers/mtd/nand/nand_bbt.c | 2 -- drivers/mtd/nand/nand_ecc.c | 2 -- drivers/mtd/nand/nand_ids.c | 2 -- drivers/mtd/nand/nandsim.c | 2 -- drivers/mtd/nand/ppchameleonevb.c | 2 -- drivers/mtd/nand/rtc_from4.c | 2 -- drivers/mtd/nand/s3c2410.c | 2 -- drivers/mtd/nand/sharpsl.c | 2 -- drivers/mtd/nand/spia.c | 2 -- drivers/mtd/nand/toto.c | 2 -- drivers/mtd/nand/ts7250.c | 2 -- drivers/mtd/nftlcore.c | 5 ----- drivers/mtd/nftlmount.c | 4 ---- drivers/mtd/redboot.c | 2 -- drivers/mtd/rfd_ftl.c | 2 -- include/linux/jffs2.h | 3 --- include/linux/mtd/blktrans.h | 2 -- include/linux/mtd/cfi.h | 1 - include/linux/mtd/cfi_endian.h | 5 ----- include/linux/mtd/concat.h | 2 -- include/linux/mtd/doc2000.h | 2 -- include/linux/mtd/flashchip.h | 3 --- include/linux/mtd/ftl.h | 2 -- include/linux/mtd/gen_probe.h | 1 - include/linux/mtd/inftl.h | 4 ---- include/linux/mtd/map.h | 1 - include/linux/mtd/mtd.h | 2 -- include/linux/mtd/nand.h | 2 -- include/linux/mtd/nand_ecc.h | 2 -- include/linux/mtd/nftl.h | 2 -- include/linux/mtd/partitions.h | 2 -- include/linux/mtd/physmap.h | 2 -- include/linux/mtd/plat-ram.h | 2 -- include/linux/mtd/pmc551.h | 4 +--- include/linux/mtd/xip.h | 2 -- include/mtd/inftl-user.h | 2 -- include/mtd/jffs2-user.h | 2 -- include/mtd/mtd-abi.h | 2 -- include/mtd/mtd-user.h | 2 -- include/mtd/nftl-user.h | 2 -- 138 files changed, 3 insertions(+), 279 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index eed06d068fd1..14f11f8b9e5f 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,5 +1,3 @@ -# $Id: Kconfig,v 1.11 2005/11/07 11:14:19 gleixner Exp $ - menuconfig MTD tristate "Memory Technology Device (MTD) support" depends on HAS_IOMEM diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c index 52d51eb91c16..d072ca5be689 100644 --- a/drivers/mtd/afs.c +++ b/drivers/mtd/afs.c @@ -21,8 +21,6 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: afs.c,v 1.15 2005/11/07 11:14:19 gleixner Exp $ - ======================================================================*/ #include diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index cc6b7bb6de02..324ff82a3cd9 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -4,8 +4,6 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.186 2005/11/23 22:07:52 nico Exp $ - * * * 10/10/2000 Nicolas Pitre * - completely revamped method functions so they are aware and diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index f7fcc6389533..a972cc6be436 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -16,9 +16,6 @@ * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com * * This code is GPL - * - * $Id: cfi_cmdset_0002.c,v 1.122 2005/11/07 11:14:22 gleixner Exp $ - * */ #include diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 1b720cc571f3..d4714dd9f7ab 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -4,8 +4,6 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0020.c,v 1.22 2005/11/07 11:14:22 gleixner Exp $ - * * 10/10/2000 Nicolas Pitre * - completely revamped method functions so they are aware and * independent of the flash geometry (buswidth, interleave, etc.) diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index a4463a91ce31..c418e92e1d92 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -1,7 +1,6 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: cfi_probe.c,v 1.86 2005/11/29 14:48:31 gleixner Exp $ */ #include diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 72e0022a47bf..0ee457018016 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -6,9 +6,6 @@ * Copyright (C) 2003 STMicroelectronics Limited * * This code is covered by the GPL. - * - * $Id: cfi_util.c,v 1.10 2005/11/07 11:14:23 gleixner Exp $ - * */ #include diff --git a/drivers/mtd/chips/chipreg.c b/drivers/mtd/chips/chipreg.c index 2174c97549f0..c85760968227 100644 --- a/drivers/mtd/chips/chipreg.c +++ b/drivers/mtd/chips/chipreg.c @@ -1,6 +1,4 @@ /* - * $Id: chipreg.c,v 1.17 2004/11/16 18:29:00 dwmw2 Exp $ - * * Registration for chip drivers * */ diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index d338b8c92780..e53a58ae384f 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -2,7 +2,6 @@ * Routines common to all CFI-type probes. * (C) 2001-2003 Red Hat, Inc. * GPL'd - * $Id: gen_probe.c,v 1.24 2005/11/07 11:14:23 gleixner Exp $ */ #include diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index b229da8060dd..afb35e3a3cee 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1,7 +1,6 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.66 2005/11/07 11:14:23 gleixner Exp $ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. diff --git a/drivers/mtd/chips/map_absent.c b/drivers/mtd/chips/map_absent.c index fc478c0f93f5..494d30d0631a 100644 --- a/drivers/mtd/chips/map_absent.c +++ b/drivers/mtd/chips/map_absent.c @@ -1,7 +1,6 @@ /* * Common code to handle absent "placeholder" devices * Copyright 2001 Resilience Corporation - * $Id: map_absent.c,v 1.6 2005/11/07 11:14:23 gleixner Exp $ * * This map driver is used to allocate "placeholder" MTD * devices on systems that have socketed/removable media. diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 5cb6d5263661..072dd8abf33a 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -1,7 +1,6 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.22 2005/01/05 18:05:12 dwmw2 Exp $ */ #include diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index cb27f855074c..821d0ed6bae3 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -1,7 +1,6 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.23 2005/01/05 18:05:12 dwmw2 Exp $ */ #include diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index e472a0e9de9d..68782ab2f0de 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -1,6 +1,4 @@ /* - * $Id: cmdlinepart.c,v 1.19 2005/11/07 11:14:19 gleixner Exp $ - * * Read flash partition table from command line * * Copyright 2002 SYSGO Real-Time Solutions GmbH diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 35ed1103dbb2..9c613f06623c 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.18 2005/11/07 11:14:24 gleixner Exp $ menu "Self-contained MTD device drivers" depends on MTD!=n diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0f788d5c4bf8..0993d5cf3923 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -1,7 +1,6 @@ # # linux/drivers/devices/Makefile # -# $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $ obj-$(CONFIG_MTD_DOC2000) += doc2000.o obj-$(CONFIG_MTD_DOC2001) += doc2001.o diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 519d942e7940..303ea9b8cfe4 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -1,6 +1,4 @@ /* - * $Id: block2mtd.c,v 1.30 2005/11/29 14:48:32 gleixner Exp $ - * * block2mtd.c - create an mtd from a block device * * Copyright (C) 2001,2002 Simon Evans @@ -20,9 +18,6 @@ #include #include -#define VERSION "$Revision: 1.30 $" - - #define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) #define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) @@ -451,7 +446,6 @@ MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,]\""); static int __init block2mtd_init(void) { int ret = 0; - INFO("version " VERSION); #ifndef MODULE if (strlen(block2mtd_paramline)) diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 846989f292e3..50de839c77a9 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -3,8 +3,6 @@ * Linux driver for Disk-On-Chip 2000 and Millennium * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse - * - * $Id: doc2000.c,v 1.67 2005/11/07 11:14:24 gleixner Exp $ */ #include diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 6413efc045e0..e32c568c1145 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -3,8 +3,6 @@ * Linux driver for Disk-On-Chip Millennium * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse - * - * $Id: doc2001.c,v 1.49 2005/11/07 11:14:24 gleixner Exp $ */ #include diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 83be3461658f..d853f891b586 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -6,8 +6,6 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * - * $Id: doc2001plus.c,v 1.14 2005/11/07 11:14:24 gleixner Exp $ - * * Released under GPL */ diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c index fd8a8daba3a8..874e51b110a2 100644 --- a/drivers/mtd/devices/docecc.c +++ b/drivers/mtd/devices/docecc.c @@ -7,8 +7,6 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: docecc.c,v 1.7 2005/11/07 11:14:25 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index d8cc94ec4e50..6e5d811ae83a 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -4,9 +4,6 @@ /* (C) 1999 Machine Vision Holdings, Inc. */ /* (C) 1999-2003 David Woodhouse */ -/* $Id: docprobe.c,v 1.46 2005/11/07 11:14:25 gleixner Exp $ */ - - /* DOC_PASSIVE_PROBE: In order to ensure that the BIOS checksum is correct at boot time, and diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 1d324e5c412d..f4bda4cee495 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -2,8 +2,6 @@ /* * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. * - * $Id: lart.c,v 1.9 2005/11/07 11:14:25 gleixner Exp $ - * * Author: Abraham vd Merwe * * Copyright (c) 2001, 2d3D, Inc. diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 9cff119a2024..6a9a24a80a6d 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -5,8 +5,6 @@ * 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. - * - * $Id: ms02-nv.c,v 1.11 2005/11/14 13:41:47 macro Exp $ */ #include diff --git a/drivers/mtd/devices/ms02-nv.h b/drivers/mtd/devices/ms02-nv.h index 8a6eef7cfee3..04deafd3a771 100644 --- a/drivers/mtd/devices/ms02-nv.h +++ b/drivers/mtd/devices/ms02-nv.h @@ -9,8 +9,6 @@ * 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. - * - * $Id: ms02-nv.h,v 1.3 2003/08/19 09:25:36 dwmw2 Exp $ */ #include diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 0399be178620..3aaca88847d3 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -1,6 +1,5 @@ /* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.37 2005/04/21 03:42:11 joern Exp $ * Author: Alexander Larsson * * Copyright (c) 1999 Alexander Larsson diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index c7987b1c5e01..088fbb7595b5 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -1,6 +1,4 @@ /** - * $Id: phram.c,v 1.16 2005/11/07 11:14:25 gleixner Exp $ - * * Copyright (c) ???? Jochen Schäuble * Copyright (c) 2003-2004 Joern Engel * diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index bc9981749064..d38bca64bb15 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -1,6 +1,4 @@ /* - * $Id: pmc551.c,v 1.32 2005/11/07 11:14:25 gleixner Exp $ - * * PMC551 PCI Mezzanine Ram Device * * Author: diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index cb86db746f28..a425d09f35a0 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -1,7 +1,5 @@ /*====================================================================== - $Id: slram.c,v 1.36 2005/11/07 11:14:25 gleixner Exp $ - This driver provides a method to access memory not used by the kernel itself (i.e. if the kernel commandline mem=xxx is used). To actually use slram at least mtdblock or mtdchar is required (for block or diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 4a79b187b568..3fed8f94ac6f 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1,5 +1,4 @@ /* This version ported to the Linux-MTD system by dwmw2@infradead.org - * $Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $ * * Fixes: Arnaldo Carvalho de Melo * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups @@ -1082,8 +1081,6 @@ static struct mtd_blktrans_ops ftl_tr = { static int init_ftl(void) { - DEBUG(0, "$Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $\n"); - return register_mtd_blktrans(&ftl_tr); } diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index b0e396504e67..c4f9d3378b24 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -7,8 +7,6 @@ * (c) 1999 Machine Vision Holdings, Inc. * Author: David Woodhouse * - * $Id: inftlcore.c,v 1.19 2005/11/07 11:14:20 gleixner Exp $ - * * 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 @@ -953,9 +951,6 @@ static struct mtd_blktrans_ops inftl_tr = { static int __init init_inftl(void) { - printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, " - "inftlmount.c %s\n", inftlmountrev); - return register_mtd_blktrans(&inftl_tr); } diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index c551d2f0779c..9113628ed1ef 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -8,8 +8,6 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: inftlmount.c,v 1.18 2005/11/07 11:14:20 gleixner Exp $ - * * 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 @@ -39,8 +37,6 @@ #include #include -char inftlmountrev[]="$Revision: 1.18 $"; - /* * find_boot_record: Find the INFTL Media Header and its Spare copy which * contains the various device information of the INFTL partition and diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 1bd69aa9e22a..b2c180245618 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.61 2005/11/07 11:14:26 gleixner Exp $ menu "Mapping drivers for chip access" depends on MTD!=n diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index a9cbe80f99a0..5444eaf4026f 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -1,7 +1,6 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile.common,v 1.34 2005/11/07 11:14:26 gleixner Exp $ ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) obj-$(CONFIG_MTD) += map_funcs.o diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 728aed6ad722..948b86f35ef4 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -2,7 +2,6 @@ * amd76xrom.c * * Normal mappings of chips in physical memory - * $Id: amd76xrom.c,v 1.21 2005/11/07 11:14:26 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/autcpu12-nvram.c b/drivers/mtd/maps/autcpu12-nvram.c index 7ed3424dd959..cf32267263df 100644 --- a/drivers/mtd/maps/autcpu12-nvram.c +++ b/drivers/mtd/maps/autcpu12-nvram.c @@ -2,8 +2,6 @@ * NV-RAM memory access on autcpu12 * (C) 2002 Thomas Gleixner (gleixner@autronix.de) * - * $Id: autcpu12-nvram.c,v 1.9 2005/11/07 11:14:26 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/maps/bast-flash.c b/drivers/mtd/maps/bast-flash.c index 1f492062f8ca..ca5414880341 100644 --- a/drivers/mtd/maps/bast-flash.c +++ b/drivers/mtd/maps/bast-flash.c @@ -9,8 +9,6 @@ * 20-Sep-2004 BJD Initial version * 17-Jan-2005 BJD Add whole device if no partitions found * - * $Id: bast-flash.c,v 1.5 2005/11/07 11:14:26 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/maps/cdb89712.c b/drivers/mtd/maps/cdb89712.c index 9f17bb6c5a9d..cb507da0a87d 100644 --- a/drivers/mtd/maps/cdb89712.c +++ b/drivers/mtd/maps/cdb89712.c @@ -1,7 +1,6 @@ /* * Flash on Cirrus CDB89712 * - * $Id: cdb89712.c,v 1.11 2005/11/07 11:14:26 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c index 629e6e2641a8..6464d487eb1a 100644 --- a/drivers/mtd/maps/ceiva.c +++ b/drivers/mtd/maps/ceiva.c @@ -11,7 +11,6 @@ * * (C) 2000 Nicolas Pitre * - * $Id: ceiva.c,v 1.11 2004/09/16 23:27:12 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index 65e5ee552010..0ecc3f6d735b 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -1,8 +1,6 @@ /* * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson * - * $Id: cfi_flagadm.c,v 1.15 2005/11/07 11:14:26 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c index 92a9c7fac993..e115667bf1d0 100644 --- a/drivers/mtd/maps/dbox2-flash.c +++ b/drivers/mtd/maps/dbox2-flash.c @@ -1,6 +1,4 @@ /* - * $Id: dbox2-flash.c,v 1.14 2005/11/07 11:14:26 gleixner Exp $ - * * D-Box 2 flash driver */ diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c index b32bb9347d71..3aa018c092f8 100644 --- a/drivers/mtd/maps/dc21285.c +++ b/drivers/mtd/maps/dc21285.c @@ -4,8 +4,6 @@ * (C) 2000 Nicolas Pitre * * This code is GPL - * - * $Id: dc21285.c,v 1.24 2005/11/07 11:14:26 gleixner Exp $ */ #include #include diff --git a/drivers/mtd/maps/dilnetpc.c b/drivers/mtd/maps/dilnetpc.c index 1c3b34ad7325..0713e3a5a22c 100644 --- a/drivers/mtd/maps/dilnetpc.c +++ b/drivers/mtd/maps/dilnetpc.c @@ -14,8 +14,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: dilnetpc.c,v 1.20 2005/11/07 11:14:26 gleixner Exp $ - * * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems * featuring the AMD Elan SC410 processor. There are two variants of this * board: DNP/1486 and ADNP/1486. The DNP version has 2 megs of flash diff --git a/drivers/mtd/maps/dmv182.c b/drivers/mtd/maps/dmv182.c index e0558b0b2fe6..d171674eb2ed 100644 --- a/drivers/mtd/maps/dmv182.c +++ b/drivers/mtd/maps/dmv182.c @@ -4,8 +4,6 @@ * * Flash map driver for the Dy4 SVME182 board * - * $Id: dmv182.c,v 1.6 2005/11/07 11:14:26 gleixner Exp $ - * * Copyright 2003-2004, TimeSys Corporation * * Based on the SVME181 flash map, by Tom Nelson, Dot4, Inc. for TimeSys Corp. diff --git a/drivers/mtd/maps/ebony.c b/drivers/mtd/maps/ebony.c index 1488bb92f26f..d92b7c70d3ed 100644 --- a/drivers/mtd/maps/ebony.c +++ b/drivers/mtd/maps/ebony.c @@ -1,6 +1,4 @@ /* - * $Id: ebony.c,v 1.16 2005/11/07 11:14:26 gleixner Exp $ - * * Mapping for Ebony user flash * * Matt Porter diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c index 1c5b97c89685..9433738c1664 100644 --- a/drivers/mtd/maps/edb7312.c +++ b/drivers/mtd/maps/edb7312.c @@ -1,6 +1,4 @@ /* - * $Id: edb7312.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $ - * * Handle mapping of the NOR flash on Cogent EDB7312 boards * * Copyright 2002 SYSGO Real-Time Solutions GmbH diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c index 7c50c271651c..a8e3fde4cbd5 100644 --- a/drivers/mtd/maps/fortunet.c +++ b/drivers/mtd/maps/fortunet.c @@ -1,6 +1,5 @@ /* fortunet.c memory map * - * $Id: fortunet.c,v 1.11 2005/11/07 11:14:27 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c index 6dde3182d64a..ef8915474462 100644 --- a/drivers/mtd/maps/h720x-flash.c +++ b/drivers/mtd/maps/h720x-flash.c @@ -2,8 +2,6 @@ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based * evaluation boards * - * $Id: h720x-flash.c,v 1.12 2005/11/07 11:14:27 gleixner Exp $ - * * (C) 2002 Jungjun Kim * 2003 Thomas Gleixner */ diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 2c884c49e84a..aeb6c916e23f 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -2,7 +2,6 @@ * ichxrom.c * * Normal mappings of chips in physical memory - * $Id: ichxrom.c,v 1.19 2005/11/07 11:14:27 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index a0b4dc7155dc..2682ab51a367 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c @@ -1,6 +1,4 @@ /* - * $Id: impa7.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $ - * * Handle mapping of the NOR flash on implementa A7 boards * * Copyright 2002 SYSGO Real-Time Solutions GmbH diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index 325c8880c437..ee361aaadb1e 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -22,8 +22,6 @@ This is access code for flashes using ARM's flash partitioning standards. - $Id: integrator-flash.c,v 1.20 2005/11/07 11:14:27 gleixner Exp $ - ======================================================================*/ #include diff --git a/drivers/mtd/maps/ipaq-flash.c b/drivers/mtd/maps/ipaq-flash.c index f27c132794c3..a806119797e0 100644 --- a/drivers/mtd/maps/ipaq-flash.c +++ b/drivers/mtd/maps/ipaq-flash.c @@ -4,8 +4,6 @@ * (C) 2000 Nicolas Pitre * (C) 2002 Hewlett-Packard Company * (C) 2003 Christian Pellegrin , : concatenation of multiple flashes - * - * $Id: ipaq-flash.c,v 1.5 2005/11/07 11:14:27 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index c8396b8574c4..c2264792a20b 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -1,6 +1,4 @@ /* - * $Id: ixp2000.c,v 1.9 2005/11/07 11:14:27 gleixner Exp $ - * * drivers/mtd/maps/ixp2000.c * * Mapping for the Intel XScale IXP2000 based systems diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 01f19a4714b5..9c7a5fbd4e51 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -1,6 +1,4 @@ /* - * $Id: ixp4xx.c,v 1.13 2005/11/16 16:23:21 dvrabel Exp $ - * * drivers/mtd/maps/ixp4xx.c * * MTD Map file for IXP4XX based systems. Please do not make per-board diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 67620adf4811..9e054503c4cf 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -1,6 +1,4 @@ /* - * $Id: l440gx.c,v 1.18 2005/11/07 11:14:27 gleixner Exp $ - * * BIOS Flash chip on Intel 440GX board. * * Bugs this currently does not work under linuxBIOS. diff --git a/drivers/mtd/maps/map_funcs.c b/drivers/mtd/maps/map_funcs.c index 9105e6ca0aa6..3f268370eeca 100644 --- a/drivers/mtd/maps/map_funcs.c +++ b/drivers/mtd/maps/map_funcs.c @@ -1,6 +1,4 @@ /* - * $Id: map_funcs.c,v 1.10 2005/06/06 23:04:36 tpoynor Exp $ - * * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS * is enabled. */ diff --git a/drivers/mtd/maps/mbx860.c b/drivers/mtd/maps/mbx860.c index 06b118727846..706f67394b07 100644 --- a/drivers/mtd/maps/mbx860.c +++ b/drivers/mtd/maps/mbx860.c @@ -1,6 +1,4 @@ /* - * $Id: mbx860.c,v 1.9 2005/11/07 11:14:27 gleixner Exp $ - * * Handle mapping of the flash on MBX860 boards * * Author: Anton Todorov diff --git a/drivers/mtd/maps/mtx-1_flash.c b/drivers/mtd/maps/mtx-1_flash.c index 2a8fde9b92f0..a3b651904127 100644 --- a/drivers/mtd/maps/mtx-1_flash.c +++ b/drivers/mtd/maps/mtx-1_flash.c @@ -1,8 +1,6 @@ /* * Flash memory access on 4G Systems MTX-1 boards * - * $Id: mtx-1_flash.c,v 1.2 2005/11/07 11:14:27 gleixner Exp $ - * * (C) 2005 Bruno Randolf * (C) 2005 Joern Engel * diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index 95dcab2146ad..c0cb319b2b70 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -3,8 +3,6 @@ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH * - * $Id: netsc520.c,v 1.14 2005/11/07 11:14:27 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 0c9b305a72e0..965e6c6d6ab0 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -5,8 +5,6 @@ * * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) - * - * $Id: nettel.c,v 1.12 2005/11/29 14:30:00 gleixner Exp $ */ /****************************************************************************/ diff --git a/drivers/mtd/maps/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c index a6642db3d325..43e04c1d22a9 100644 --- a/drivers/mtd/maps/octagon-5066.c +++ b/drivers/mtd/maps/octagon-5066.c @@ -1,4 +1,3 @@ -// $Id: octagon-5066.c,v 1.28 2005/11/07 11:14:27 gleixner Exp $ /* ###################################################################### Octagon 5066 MTD Driver. diff --git a/drivers/mtd/maps/omap-toto-flash.c b/drivers/mtd/maps/omap-toto-flash.c index e6e391efbeb6..0a60ebbc2175 100644 --- a/drivers/mtd/maps/omap-toto-flash.c +++ b/drivers/mtd/maps/omap-toto-flash.c @@ -4,8 +4,6 @@ * jzhang@ti.com (C) 2003 Texas Instruments. * * (C) 2002 MontVista Software, Inc. - * - * $Id: omap-toto-flash.c,v 1.5 2005/11/07 11:14:27 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index d2ab1bae9c34..5c6a25c90380 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -7,8 +7,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * $Id: pci.c,v 1.14 2005/11/17 08:20:27 dwmw2 Exp $ - * * Generic PCI memory map driver. We support the following boards: * - Intel IQ80310 ATU. * - Intel EBSA285 (blank rom programming mode). Tested working 27/09/2001 diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 1912d968718b..8f7ca863f89d 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -1,6 +1,4 @@ /* - * $Id: pcmciamtd.c,v 1.55 2005/11/07 11:14:28 gleixner Exp $ - * * pcmciamtd.c - MTD driver for PCMCIA flash memory cards * * Author: Simon Evans @@ -48,7 +46,6 @@ static const int debug = 0; #define DRIVER_DESC "PCMCIA Flash memory card driver" -#define DRIVER_VERSION "$Revision: 1.55 $" /* Size of the PCMCIA address space: 26 bits = 64 MB */ #define MAX_PCMCIA_ADDR 0x4000000 @@ -790,7 +787,7 @@ static struct pcmcia_driver pcmciamtd_driver = { static int __init init_pcmciamtd(void) { - info(DRIVER_DESC " " DRIVER_VERSION); + info(DRIVER_DESC); if(bankwidth && bankwidth != 1 && bankwidth != 2) { info("bad bankwidth (%d), using default", bankwidth); diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 183255fcfdcb..1f6b9066b63e 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -1,6 +1,4 @@ /* - * $Id: physmap.c,v 1.39 2005/11/29 14:49:36 gleixner Exp $ - * * Normal mappings of chips in physical memory * * Copyright (C) 2003 MontaVista Software Inc. diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 3eb2643b2328..e7dd9c8a965e 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -6,8 +6,6 @@ * * Generic platfrom device based RAM map * - * $Id: plat-ram.c,v 1.7 2005/11/07 11:14:28 gleixner Exp $ - * * 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 diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c index 4d858b3d5f82..de002eb1a7fe 100644 --- a/drivers/mtd/maps/redwood.c +++ b/drivers/mtd/maps/redwood.c @@ -1,6 +1,4 @@ /* - * $Id: redwood.c,v 1.11 2005/11/07 11:14:28 gleixner Exp $ - * * drivers/mtd/maps/redwood.c * * FLASH map for the IBM Redwood 4/5/6 boards. diff --git a/drivers/mtd/maps/rpxlite.c b/drivers/mtd/maps/rpxlite.c index 809a0c8e7aaf..14d90edb4430 100644 --- a/drivers/mtd/maps/rpxlite.c +++ b/drivers/mtd/maps/rpxlite.c @@ -1,6 +1,4 @@ /* - * $Id: rpxlite.c,v 1.22 2004/11/04 13:24:15 gleixner Exp $ - * * Handle mapping of the flash on the RPX Lite and CLLF boards */ diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index c7d5a52a2d55..e177a43dfff0 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -2,8 +2,6 @@ * Flash memory access on SA11x0 based devices * * (C) 2000 Nicolas Pitre - * - * $Id: sa1100-flash.c,v 1.51 2005/11/07 11:14:28 gleixner Exp $ */ #include #include diff --git a/drivers/mtd/maps/sbc8240.c b/drivers/mtd/maps/sbc8240.c index b8c1331b7a04..6e1e99cd2b59 100644 --- a/drivers/mtd/maps/sbc8240.c +++ b/drivers/mtd/maps/sbc8240.c @@ -4,9 +4,6 @@ * Carolyn Smith, Tektronix, Inc. * * This code is GPLed - * - * $Id: sbc8240.c,v 1.5 2005/11/07 11:14:28 gleixner Exp $ - * */ /* diff --git a/drivers/mtd/maps/sbc_gxx.c b/drivers/mtd/maps/sbc_gxx.c index 7cc4041d096d..1b1c0b7e11ef 100644 --- a/drivers/mtd/maps/sbc_gxx.c +++ b/drivers/mtd/maps/sbc_gxx.c @@ -17,8 +17,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - $Id: sbc_gxx.c,v 1.35 2005/11/07 11:14:28 gleixner Exp $ - The SBC-MediaGX / SBC-GXx has up to 16 MiB of Intel StrataFlash (28F320/28F640) in x8 mode. diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index 4045e372b90d..85c1e56309ec 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -16,8 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: sc520cdp.c,v 1.23 2005/11/17 08:20:27 dwmw2 Exp $ - * * * The SC520CDP is an evaluation board for the Elan SC520 processor available * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size, diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 0fc5584324e3..21169e6d646c 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -1,6 +1,5 @@ /* * MTD map driver for BIOS Flash on Intel SCB2 boards - * $Id: scb2_flash.c,v 1.12 2005/03/18 14:04:35 gleixner Exp $ * Copyright (C) 2002 Sun Microsystems, Inc. * Tim Hockin * diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c index 5e2bce22f37c..b5391ebb736e 100644 --- a/drivers/mtd/maps/scx200_docflash.c +++ b/drivers/mtd/maps/scx200_docflash.c @@ -2,8 +2,6 @@ Copyright (c) 2001,2002 Christer Weinigel - $Id: scx200_docflash.c,v 1.12 2005/11/07 11:14:28 gleixner Exp $ - National Semiconductor SCx200 flash mapped with DOCCS */ diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c index 917dc778f24e..026eab028189 100644 --- a/drivers/mtd/maps/sharpsl-flash.c +++ b/drivers/mtd/maps/sharpsl-flash.c @@ -4,8 +4,6 @@ * Copyright (C) 2001 Lineo Japan, Inc. * Copyright (C) 2002 SHARP * - * $Id: sharpsl-flash.c,v 1.7 2005/11/07 11:14:28 gleixner Exp $ - * * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp * Handle mapping of the flash on the RPX Lite and CLLF boards * diff --git a/drivers/mtd/maps/solutionengine.c b/drivers/mtd/maps/solutionengine.c index d76ceef453ce..0eb41d9c6786 100644 --- a/drivers/mtd/maps/solutionengine.c +++ b/drivers/mtd/maps/solutionengine.c @@ -1,6 +1,4 @@ /* - * $Id: solutionengine.c,v 1.15 2005/11/07 11:14:28 gleixner Exp $ - * * Flash and EPROM on Hitachi Solution Engine and similar boards. * * (C) 2001 Red Hat, Inc. diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 001af7f7ddda..0d7c88396c88 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -1,4 +1,4 @@ -/* $Id: sun_uflash.c,v 1.13 2005/11/07 11:14:28 gleixner Exp $ +/* * * sun_uflash - Driver implementation for user-programmable flash * present on many Sun Microsystems SME boardsets. diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index 521734057314..a5d3d8531faa 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c @@ -2,8 +2,6 @@ * Handle mapping of the flash memory access routines * on TQM8xxL based devices. * - * $Id: tqm8xxl.c,v 1.15 2005/11/07 11:14:28 gleixner Exp $ - * * based on rpxlite.c * * Copyright(C) 2001 Kirk Lee diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c index b47270e850bc..e2147bf11c88 100644 --- a/drivers/mtd/maps/ts5500_flash.c +++ b/drivers/mtd/maps/ts5500_flash.c @@ -22,8 +22,6 @@ * - Drive A and B use the resident flash disk (RFD) flash translation layer. * - If you have created your own jffs file system and the bios overwrites * it during boot, try disabling Drive A: and B: in the boot order. - * - * $Id: ts5500_flash.c,v 1.5 2005/11/07 11:14:28 gleixner Exp $ */ #include diff --git a/drivers/mtd/maps/tsunami_flash.c b/drivers/mtd/maps/tsunami_flash.c index 0f915ac3102e..77a8bfc02577 100644 --- a/drivers/mtd/maps/tsunami_flash.c +++ b/drivers/mtd/maps/tsunami_flash.c @@ -2,7 +2,6 @@ * tsunami_flash.c * * flash chip on alpha ds10... - * $Id: tsunami_flash.c,v 1.10 2005/11/07 11:14:29 gleixner Exp $ */ #include #include diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index c42f4b83f686..bac000a88313 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -4,8 +4,6 @@ * uclinux.c -- generic memory mapped MTD driver for uclinux * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) - * - * $Id: uclinux.c,v 1.12 2005/11/07 11:14:29 gleixner Exp $ */ /****************************************************************************/ diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c index b3e487395435..5a0c9a353b0f 100644 --- a/drivers/mtd/maps/vmax301.c +++ b/drivers/mtd/maps/vmax301.c @@ -1,4 +1,3 @@ -// $Id: vmax301.c,v 1.32 2005/11/07 11:14:29 gleixner Exp $ /* ###################################################################### Tempustech VMAX SBC301 MTD Driver. diff --git a/drivers/mtd/maps/walnut.c b/drivers/mtd/maps/walnut.c index ca932122fb64..e243476c8171 100644 --- a/drivers/mtd/maps/walnut.c +++ b/drivers/mtd/maps/walnut.c @@ -1,6 +1,4 @@ /* - * $Id: walnut.c,v 1.3 2005/11/07 11:14:29 gleixner Exp $ - * * Mapping for Walnut flash * (used ebony.c as a "framework") * diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c index ac5b8105b6ef..413b0cf9bbd2 100644 --- a/drivers/mtd/maps/wr_sbc82xx_flash.c +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -1,6 +1,4 @@ /* - * $Id: wr_sbc82xx_flash.c,v 1.8 2005/11/07 11:14:29 gleixner Exp $ - * * Map for flash chips on Wind River PowerQUICC II SBC82xx board. * * Copyright (C) 2004 Red Hat, Inc. diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 839eed8430a2..a0ada45672d8 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -1,6 +1,4 @@ /* - * $Id: mtd_blkdevs.c,v 1.27 2005/11/07 11:14:20 gleixner Exp $ - * * (C) 2003 David Woodhouse * * Interface to Linux 2.5 block layer for MTD 'translation layers'. diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 952da30b1745..208c6faa0358 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -1,8 +1,6 @@ /* * Direct MTD block device access * - * $Id: mtdblock.c,v 1.68 2005/11/07 11:14:20 gleixner Exp $ - * * (C) 2000-2003 Nicolas Pitre * (C) 1999-2003 David Woodhouse */ diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index f79dbb49b1a2..852165f8b1c3 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -1,6 +1,4 @@ /* - * $Id: mtdblock_ro.c,v 1.19 2004/11/16 18:28:59 dwmw2 Exp $ - * * (C) 2003 David Woodhouse * * Simple read-only (writable only for RAM) mtdblock driver diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5d3ac512ce16..4b3156f9b36f 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1,6 +1,4 @@ /* - * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $ - * * Character-device access to raw MTD devices. * */ diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index d563dcd4b264..2972a5edb73d 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -6,8 +6,6 @@ * NAND support by Christian Gan * * This code is GPL - * - * $Id: mtdconcat.c,v 1.11 2005/11/07 11:14:20 gleixner Exp $ */ #include diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 8c61035b968b..4373790401d3 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1,6 +1,4 @@ /* - * $Id: mtdcore.c,v 1.47 2005/11/07 11:14:20 gleixner Exp $ - * * Core registration and callback routines for MTD * drivers and users. * diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 07c701169344..11b803cc405b 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -5,8 +5,6 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.55 2005/11/07 11:14:20 gleixner Exp $ - * * 02-21-2002 Thomas Gleixner * added support for read_oob, write_oob */ diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5076faf9ca66..7dea6c3a6603 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $ menuconfig MTD_NAND tristate "NAND Device Support" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index a6e74a46992a..d95a10c51866 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,7 +1,6 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 09e421a96893..22ad9f367760 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -3,8 +3,6 @@ * * Copyright (C) 2004 Embedded Edge, LLC * - * $Id: au1550nd.c,v 1.13 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c index dd38011ee0b7..553dd7e9b41c 100644 --- a/drivers/mtd/nand/autcpu12.c +++ b/drivers/mtd/nand/autcpu12.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: autcpu12.c,v 1.23 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 0e72153b3297..cd4393ce2562 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -15,8 +15,6 @@ * converted to the generic Reed-Solomon library by Thomas Gleixner * * Interface to generic NAND code for M-Systems DiskOnChip devices - * - * $Id: diskonchip.c,v 1.55 2005/11/07 11:14:30 gleixner Exp $ */ #include diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index ba67bbec20d3..387e4352903e 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: edb7312.c,v 1.12 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 2d585d2d090c..9e59de501c2e 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -7,8 +7,6 @@ * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: h1910.c,v 1.6 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 5e121ceaa598..0b1c48595f12 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -6,8 +6,6 @@ * * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 9003a135e050..918a806a8471 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -9,8 +9,6 @@ * * Copyright (C) 2006 Thomas Gleixner * - * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $ - * * This file 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 or (at your option) any diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index a3e3ab0185d5..69ee2c90eb0b 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -3,8 +3,6 @@ * * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_ids.c,v 1.16 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 68c150c8ff9d..add975a229a7 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -21,8 +21,6 @@ * 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 - * - * $Id: nandsim.c,v 1.8 2005/03/19 15:33:56 dedekind Exp $ */ #include diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c index 082073acf20f..cc8658431851 100644 --- a/drivers/mtd/nand/ppchameleonevb.c +++ b/drivers/mtd/nand/ppchameleonevb.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/edb7312.c * * - * $Id: ppchameleonevb.c,v 1.7 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 26f88215bc47..a033c4cd8e16 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: rtc_from4.c,v 1.10 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index b34a460ab679..91f42e485520 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -20,8 +20,6 @@ * 20-Oct-2005 BJD Fix timing calculation bug * 14-Jan-2006 BJD Allow clock to be stopped when idle * - * $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 bjd Exp $ - * * 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 diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 033f8800b1e6..6dba2fb66ae5 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -3,8 +3,6 @@ * * Copyright (C) 2004 Richard Purdie * - * $Id: sharpsl.c,v 1.7 2005/11/07 11:14:31 gleixner Exp $ - * * Based on Sharp's NAND driver sharp_sl.c * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c index 1f6d429b1583..0cc6d0acb8fe 100644 --- a/drivers/mtd/nand/spia.c +++ b/drivers/mtd/nand/spia.c @@ -8,8 +8,6 @@ * to controllines (due to change in nand.c) * page_cache added * - * $Id: spia.c,v 1.25 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/toto.c b/drivers/mtd/nand/toto.c index f9e2d4a0ab8c..bbf492e6830d 100644 --- a/drivers/mtd/nand/toto.c +++ b/drivers/mtd/nand/toto.c @@ -14,8 +14,6 @@ * Overview: * This is a device driver for the NAND flash device found on the * TI fido board. It supports 32MiB and 64MiB cards - * - * $Id: toto.c,v 1.5 2005/11/07 11:14:31 gleixner Exp $ */ #include diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c index f40081069ab2..807a72752eeb 100644 --- a/drivers/mtd/nand/ts7250.c +++ b/drivers/mtd/nand/ts7250.c @@ -9,8 +9,6 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: ts7250.c,v 1.4 2004/12/30 22:02:07 joff Exp $ - * * 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. diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 0c9ce19ea27a..320b929abe79 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -1,7 +1,6 @@ /* Linux driver for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse */ -/* $Id: nftlcore.c,v 1.98 2005/11/07 11:14:21 gleixner Exp $ */ /* The contents of this file are distributed under the GNU General @@ -803,12 +802,8 @@ static struct mtd_blktrans_ops nftl_tr = { .owner = THIS_MODULE, }; -extern char nftlmountrev[]; - static int __init init_nftl(void) { - printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev); - return register_mtd_blktrans(&nftl_tr); } diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 345e6eff89ce..ccc4f209fbb5 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -4,8 +4,6 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: nftlmount.c,v 1.41 2005/11/07 11:14:21 gleixner Exp $ - * * 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 @@ -31,8 +29,6 @@ #define SECTORSIZE 512 -char nftlmountrev[]="$Revision: 1.41 $"; - /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the * various device information of the NFTL partition and Bad Unit Table. Update * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[] diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 47474903263c..5afa268c02f1 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -1,6 +1,4 @@ /* - * $Id: redboot.c,v 1.21 2006/03/30 18:34:37 bjd Exp $ - * * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. */ diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index c84e45465499..e538c0a72abb 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -3,8 +3,6 @@ * * Copyright (C) 2005 Sean Young * - * $Id: rfd_ftl.c,v 1.8 2006/01/15 12:51:44 sean Exp $ - * * This type of flash translation layer (FTL) is used by the Embedded BIOS * by General Software. It is known as the Resident Flash Disk (RFD), see: * diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 6b563cae23df..da720bc3eb15 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -7,9 +7,6 @@ * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. - * - * $Id: jffs2.h,v 1.38 2005/09/26 11:37:23 havasi Exp $ - * */ #ifndef __LINUX_JFFS2_H__ diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h index 9a6e2f953cba..310e61606415 100644 --- a/include/linux/mtd/blktrans.h +++ b/include/linux/mtd/blktrans.h @@ -1,6 +1,4 @@ /* - * $Id: blktrans.h,v 1.6 2005/11/07 11:14:54 gleixner Exp $ - * * (C) 2003 David Woodhouse * * Interface to Linux block layer for MTD 'translation layers'. diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index b0ddf4b25862..d6fb115f5a07 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -1,7 +1,6 @@ /* Common Flash Interface structures * See http://support.intel.com/design/flash/technote/index.htm - * $Id: cfi.h,v 1.57 2005/11/15 23:28:17 tpoynor Exp $ */ #ifndef __MTD_CFI_H__ diff --git a/include/linux/mtd/cfi_endian.h b/include/linux/mtd/cfi_endian.h index 25724f7d3867..d802f7736be3 100644 --- a/include/linux/mtd/cfi_endian.h +++ b/include/linux/mtd/cfi_endian.h @@ -1,8 +1,3 @@ -/* - * $Id: cfi_endian.h,v 1.11 2002/01/30 23:20:48 awozniak Exp $ - * - */ - #include #ifndef CONFIG_MTD_CFI_ADV_OPTIONS diff --git a/include/linux/mtd/concat.h b/include/linux/mtd/concat.h index ed8dc6755219..c02f3d264ecf 100644 --- a/include/linux/mtd/concat.h +++ b/include/linux/mtd/concat.h @@ -4,8 +4,6 @@ * (C) 2002 Robert Kaiser * * This code is GPL - * - * $Id: concat.h,v 1.1 2002/03/08 16:34:36 rkaiser Exp $ */ #ifndef MTD_CONCAT_H diff --git a/include/linux/mtd/doc2000.h b/include/linux/mtd/doc2000.h index 9addd073bf15..0a6d516ab71d 100644 --- a/include/linux/mtd/doc2000.h +++ b/include/linux/mtd/doc2000.h @@ -6,8 +6,6 @@ * Copyright (C) 2002-2003 Greg Ungerer * Copyright (C) 2002-2003 SnapGear Inc * - * $Id: doc2000.h,v 1.25 2005/11/07 11:14:54 gleixner Exp $ - * * Released under GPL */ diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index 39e7d2a1be9a..08dd131301c1 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h @@ -5,9 +5,6 @@ * Contains information about the location and state of a given flash device * * (C) 2000 Red Hat. GPLd. - * - * $Id: flashchip.h,v 1.18 2005/11/07 11:14:54 gleixner Exp $ - * */ #ifndef __MTD_FLASHCHIP_H__ diff --git a/include/linux/mtd/ftl.h b/include/linux/mtd/ftl.h index d99609113307..0be442f881dd 100644 --- a/include/linux/mtd/ftl.h +++ b/include/linux/mtd/ftl.h @@ -1,6 +1,4 @@ /* - * $Id: ftl.h,v 1.7 2005/11/07 11:14:54 gleixner Exp $ - * * Derived from (and probably identical to): * ftl.h 1.7 1999/10/25 20:23:17 * diff --git a/include/linux/mtd/gen_probe.h b/include/linux/mtd/gen_probe.h index 256e7342ed1e..df362ddf2949 100644 --- a/include/linux/mtd/gen_probe.h +++ b/include/linux/mtd/gen_probe.h @@ -1,7 +1,6 @@ /* * (C) 2001, 2001 Red Hat, Inc. * GPL'd - * $Id: gen_probe.h,v 1.4 2005/11/07 11:14:54 gleixner Exp $ */ #ifndef __LINUX_MTD_GEN_PROBE_H__ diff --git a/include/linux/mtd/inftl.h b/include/linux/mtd/inftl.h index 85fd041d44ad..64ee53ce95a9 100644 --- a/include/linux/mtd/inftl.h +++ b/include/linux/mtd/inftl.h @@ -2,8 +2,6 @@ * inftl.h -- defines to support the Inverse NAND Flash Translation Layer * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) - * - * $Id: inftl.h,v 1.7 2005/06/13 13:08:45 sean Exp $ */ #ifndef __MTD_INFTL_H__ @@ -52,8 +50,6 @@ struct INFTLrecord { int INFTL_mount(struct INFTLrecord *s); int INFTL_formatblock(struct INFTLrecord *s, int block); -extern char inftlmountrev[]; - void INFTL_dumptables(struct INFTLrecord *s); void INFTL_dumpVUchains(struct INFTLrecord *s); diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index a9fae032ba81..85e3939cf487 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -1,6 +1,5 @@ /* Overhauled routines for dealing with different mmap regions of flash */ -/* $Id: map.h,v 1.54 2005/11/07 11:14:54 gleixner Exp $ */ #ifndef __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 245f9098e171..31ed234b2a74 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -1,6 +1,4 @@ /* - * $Id: mtd.h,v 1.61 2005/11/07 11:14:54 gleixner Exp $ - * * Copyright (C) 1999-2003 David Woodhouse et al. * * Released under GPL diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index c42bc7f533a5..1288be7b7740 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -5,8 +5,6 @@ * Steven J. Hill * Thomas Gleixner * - * $Id: nand.h,v 1.74 2005/09/15 13:58:50 vwool Exp $ - * * 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. diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index 12c5bc342ead..090da505425d 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h @@ -3,8 +3,6 @@ * * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: nand_ecc.h,v 1.4 2004/06/17 02:35:02 dbrown Exp $ - * * 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. diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h index 001eec50cac6..dcaf611ed748 100644 --- a/include/linux/mtd/nftl.h +++ b/include/linux/mtd/nftl.h @@ -1,6 +1,4 @@ /* - * $Id: nftl.h,v 1.16 2004/06/30 14:49:00 dbrown Exp $ - * * (C) 1999-2003 David Woodhouse */ diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 7c37d7e55abc..5014f7a9f5df 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -4,8 +4,6 @@ * (C) 2000 Nicolas Pitre * * This code is GPL - * - * $Id: partitions.h,v 1.17 2005/11/07 11:14:55 gleixner Exp $ */ #ifndef MTD_PARTITIONS_H diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index 0dc07d5f3354..c8e63a5ee72e 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h @@ -2,8 +2,6 @@ * For boards with physically mapped flash and using * drivers/mtd/maps/physmap.c mapping driver. * - * $Id: physmap.h,v 1.4 2005/11/07 11:14:55 gleixner Exp $ - * * Copyright (C) 2003 MontaVista Software Inc. * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net * diff --git a/include/linux/mtd/plat-ram.h b/include/linux/mtd/plat-ram.h index 0e37ad07bce2..e07890aff1cf 100644 --- a/include/linux/mtd/plat-ram.h +++ b/include/linux/mtd/plat-ram.h @@ -6,8 +6,6 @@ * * Generic platform device based RAM map * - * $Id: plat-ram.h,v 1.2 2005/01/24 00:37:40 bjd Exp $ - * * 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. diff --git a/include/linux/mtd/pmc551.h b/include/linux/mtd/pmc551.h index 5cc070c24d88..27ad40aed19f 100644 --- a/include/linux/mtd/pmc551.h +++ b/include/linux/mtd/pmc551.h @@ -1,6 +1,4 @@ /* - * $Id: pmc551.h,v 1.6 2005/11/07 11:14:55 gleixner Exp $ - * * PMC551 PCI Mezzanine Ram Device * * Author: @@ -17,7 +15,7 @@ #include -#define PMC551_VERSION "$Id: pmc551.h,v 1.6 2005/11/07 11:14:55 gleixner Exp $\n"\ +#define PMC551_VERSION \ "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n" /* diff --git a/include/linux/mtd/xip.h b/include/linux/mtd/xip.h index e9d40bdde48c..36efcba15ecd 100644 --- a/include/linux/mtd/xip.h +++ b/include/linux/mtd/xip.h @@ -11,8 +11,6 @@ * 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. - * - * $Id: xip.h,v 1.5 2005/11/07 11:14:55 gleixner Exp $ */ #ifndef __LINUX_MTD_XIP_H__ diff --git a/include/mtd/inftl-user.h b/include/mtd/inftl-user.h index 9b1e2526b45e..e17eda302b2d 100644 --- a/include/mtd/inftl-user.h +++ b/include/mtd/inftl-user.h @@ -1,6 +1,4 @@ /* - * $Id: inftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $ - * * Parts of INFTL headers shared with userspace * */ diff --git a/include/mtd/jffs2-user.h b/include/mtd/jffs2-user.h index d508ef0ae091..001685d7fa88 100644 --- a/include/mtd/jffs2-user.h +++ b/include/mtd/jffs2-user.h @@ -1,6 +1,4 @@ /* - * $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $ - * * JFFS2 definitions for use in user space only */ diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 615072c4da04..c6c61cd5a254 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -1,6 +1,4 @@ /* - * $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $ - * * Portions of MTD ABI definition which are shared by kernel and user space */ diff --git a/include/mtd/mtd-user.h b/include/mtd/mtd-user.h index 713f34d3e62e..170ceca3b2d0 100644 --- a/include/mtd/mtd-user.h +++ b/include/mtd/mtd-user.h @@ -1,6 +1,4 @@ /* - * $Id: mtd-user.h,v 1.2 2004/05/05 14:44:57 dwmw2 Exp $ - * * MTD ABI header for use by user space only. */ diff --git a/include/mtd/nftl-user.h b/include/mtd/nftl-user.h index b2bca18e7311..390d21c080aa 100644 --- a/include/mtd/nftl-user.h +++ b/include/mtd/nftl-user.h @@ -1,6 +1,4 @@ /* - * $Id: nftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $ - * * Parts of NFTL headers shared with userspace * */ -- cgit From a0da84f35b25875870270d16b6eccda4884d61a7 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:22 +1000 Subject: Improve setting of "events_cleared" for write-intent bitmaps. When an array is degraded, bits in the write-intent bitmap are not cleared, so that if the missing device is re-added, it can be synced by only updated those parts of the device that have changed since it was removed. The enable this a 'events_cleared' value is stored. It is the event counter for the array the last time that any bits were cleared. Sometimes - if a device disappears from an array while it is 'clean' - the events_cleared value gets updated incorrectly (there are subtle ordering issues between updateing events in the main metadata and the bitmap metadata) resulting in the missing device appearing to require a full resync when it is re-added. With this patch, we update events_cleared precisely when we are about to clear a bit in the bitmap. We record events_cleared when we clear the bit internally, and copy that to the superblock which is written out before the bit on storage. This makes it more "obviously correct". We also need to update events_cleared when the event_count is going backwards (as happens on a dirty->clean transition of a non-degraded array). Thanks to Mike Snitzer for identifying this problem and testing early "fixes". Cc: "Mike Snitzer" Signed-off-by: Neil Brown --- drivers/md/bitmap.c | 29 ++++++++++++++++++++++++----- include/linux/raid/bitmap.h | 1 + 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index b26927ce889c..dedba16d42f7 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -454,8 +454,11 @@ void bitmap_update_sb(struct bitmap *bitmap) spin_unlock_irqrestore(&bitmap->lock, flags); sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0); sb->events = cpu_to_le64(bitmap->mddev->events); - if (!bitmap->mddev->degraded) - sb->events_cleared = cpu_to_le64(bitmap->mddev->events); + if (bitmap->mddev->events < bitmap->events_cleared) { + /* rocking back to read-only */ + bitmap->events_cleared = bitmap->mddev->events; + sb->events_cleared = cpu_to_le64(bitmap->events_cleared); + } kunmap_atomic(sb, KM_USER0); write_page(bitmap, bitmap->sb_page, 1); } @@ -1085,9 +1088,19 @@ void bitmap_daemon_work(struct bitmap *bitmap) } else spin_unlock_irqrestore(&bitmap->lock, flags); lastpage = page; -/* - printk("bitmap clean at page %lu\n", j); -*/ + + /* We are possibly going to clear some bits, so make + * sure that events_cleared is up-to-date. + */ + if (bitmap->need_sync) { + bitmap_super_t *sb; + bitmap->need_sync = 0; + sb = kmap_atomic(bitmap->sb_page, KM_USER0); + sb->events_cleared = + cpu_to_le64(bitmap->events_cleared); + kunmap_atomic(sb, KM_USER0); + write_page(bitmap, bitmap->sb_page, 1); + } spin_lock_irqsave(&bitmap->lock, flags); clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN); } @@ -1257,6 +1270,12 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto return; } + if (success && + bitmap->events_cleared < bitmap->mddev->events) { + bitmap->events_cleared = bitmap->mddev->events; + bitmap->need_sync = 1; + } + if (!success && ! (*bmc & NEEDED_MASK)) *bmc |= NEEDED_MASK; diff --git a/include/linux/raid/bitmap.h b/include/linux/raid/bitmap.h index 78bfdea24a8e..e98900671ca9 100644 --- a/include/linux/raid/bitmap.h +++ b/include/linux/raid/bitmap.h @@ -221,6 +221,7 @@ struct bitmap { unsigned long syncchunk; __u64 events_cleared; + int need_sync; /* bitmap spinlock */ spinlock_t lock; -- cgit From 5e96ee65c8bd629ce093da67a066d3946468298a Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:24 +1000 Subject: Allow setting start point for requested check/repair This makes it possible to just resync a small part of an array. e.g. if a drive reports that it has questionable sectors, a 'repair' of just the region covering those sectors will cause them to be read and, if there is an error, re-written with correct data. Signed-off-by: Neil Brown --- drivers/md/md.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- include/linux/raid/md_k.h | 2 ++ 2 files changed, 44 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index 2580ac1b9b0f..261322722c19 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -278,6 +278,7 @@ static mddev_t * mddev_find(dev_t unit) init_waitqueue_head(&new->sb_wait); init_waitqueue_head(&new->recovery_wait); new->reshape_position = MaxSector; + new->resync_min = 0; new->resync_max = MaxSector; new->level = LEVEL_NONE; @@ -3074,6 +3075,36 @@ sync_completed_show(mddev_t *mddev, char *page) static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed); +static ssize_t +min_sync_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%llu\n", + (unsigned long long)mddev->resync_min); +} +static ssize_t +min_sync_store(mddev_t *mddev, const char *buf, size_t len) +{ + unsigned long long min; + if (strict_strtoull(buf, 10, &min)) + return -EINVAL; + if (min > mddev->resync_max) + return -EINVAL; + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) + return -EBUSY; + + /* Must be a multiple of chunk_size */ + if (mddev->chunk_size) { + if (min & (sector_t)((mddev->chunk_size>>9)-1)) + return -EINVAL; + } + mddev->resync_min = min; + + return len; +} + +static struct md_sysfs_entry md_min_sync = +__ATTR(sync_min, S_IRUGO|S_IWUSR, min_sync_show, min_sync_store); + static ssize_t max_sync_show(mddev_t *mddev, char *page) { @@ -3089,9 +3120,10 @@ max_sync_store(mddev_t *mddev, const char *buf, size_t len) if (strncmp(buf, "max", 3) == 0) mddev->resync_max = MaxSector; else { - char *ep; - unsigned long long max = simple_strtoull(buf, &ep, 10); - if (ep == buf || (*ep != 0 && *ep != '\n')) + unsigned long long max; + if (strict_strtoull(buf, 10, &max)) + return -EINVAL; + if (max < mddev->resync_min) return -EINVAL; if (max < mddev->resync_max && test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) @@ -3222,6 +3254,7 @@ static struct attribute *md_redundancy_attrs[] = { &md_sync_speed.attr, &md_sync_force_parallel.attr, &md_sync_completed.attr, + &md_min_sync.attr, &md_max_sync.attr, &md_suspend_lo.attr, &md_suspend_hi.attr, @@ -3777,6 +3810,7 @@ static int do_md_stop(mddev_t * mddev, int mode) mddev->size = 0; mddev->raid_disks = 0; mddev->recovery_cp = 0; + mddev->resync_min = 0; mddev->resync_max = MaxSector; mddev->reshape_position = MaxSector; mddev->external = 0; @@ -5625,9 +5659,11 @@ void md_do_sync(mddev_t *mddev) max_sectors = mddev->resync_max_sectors; mddev->resync_mismatches = 0; /* we don't use the checkpoint if there's a bitmap */ - if (!mddev->bitmap && - !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) + if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) + j = mddev->resync_min; + else if (!mddev->bitmap) j = mddev->recovery_cp; + } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) max_sectors = mddev->size << 1; else { @@ -5796,6 +5832,7 @@ void md_do_sync(mddev_t *mddev) skip: mddev->curr_resync = 0; + mddev->resync_min = 0; mddev->resync_max = MaxSector; sysfs_notify(&mddev->kobj, NULL, "sync_completed"); wake_up(&resync_wait); diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 3dea9f545c8f..780e0613e6d5 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -227,6 +227,8 @@ struct mddev_s atomic_t recovery_active; /* blocks scheduled, but not written */ wait_queue_head_t recovery_wait; sector_t recovery_cp; + sector_t resync_min; /* user requested sync + * starts here */ sector_t resync_max; /* resync should pause * when it gets here */ -- cgit From 72a23c211e4587859d5bf61ac4962d76e593fb02 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:41 +1000 Subject: Make sure all changes to md/sync_action are notified. When the 'resync' thread starts or stops, when we explicitly set sync_action, or when we determine that there is definitely nothing to do, we notify sync_action. To stop "sync_action" from occasionally showing the wrong value, we introduce a new flags - MD_RECOVERY_RECOVER - to say that a recovery is probably needed or happening, and we make sure that we set MD_RECOVERY_RUNNING before clearing MD_RECOVERY_NEEDED. Signed-off-by: Neil Brown --- Documentation/md.txt | 6 ++++++ drivers/md/md.c | 34 ++++++++++++++++++++++++++++------ include/linux/raid/md_k.h | 2 ++ 3 files changed, 36 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/Documentation/md.txt b/Documentation/md.txt index dca97ba4944a..c05bfb55659e 100644 --- a/Documentation/md.txt +++ b/Documentation/md.txt @@ -386,6 +386,12 @@ also have 'check' and 'repair' will start the appropriate process providing the current state is 'idle'. + This file responds to select/poll. Any important change in the value + triggers a poll event. Sometimes the value will briefly be + "recover" if a recovery seems to be needed, but cannot be + achieved. In that case, the transition to "recover" isn't + notified, but the transition away is. + mismatch_count When performing 'check' and 'repair', and possibly when performing 'resync', md will count the number of errors that are diff --git a/drivers/md/md.c b/drivers/md/md.c index 5b9d4fe4e6e4..c26dcad8a3ac 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -169,7 +169,6 @@ void md_new_event(mddev_t *mddev) { atomic_inc(&md_event_count); wake_up(&md_event_waiters); - sysfs_notify(&mddev->kobj, NULL, "sync_action"); } EXPORT_SYMBOL_GPL(md_new_event); @@ -2936,7 +2935,7 @@ action_show(mddev_t *mddev, char *page) type = "check"; else type = "repair"; - } else + } else if (test_bit(MD_RECOVERY_RECOVER, &mddev->recovery)) type = "recover"; } return sprintf(page, "%s\n", type); @@ -2958,9 +2957,12 @@ action_store(mddev_t *mddev, const char *page, size_t len) } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) return -EBUSY; - else if (cmd_match(page, "resync") || cmd_match(page, "recover")) + else if (cmd_match(page, "resync")) + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + else if (cmd_match(page, "recover")) { + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); - else if (cmd_match(page, "reshape")) { + } else if (cmd_match(page, "reshape")) { int err; if (mddev->pers->start_reshape == NULL) return -EINVAL; @@ -2977,6 +2979,7 @@ action_store(mddev_t *mddev, const char *page, size_t len) } set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); return len; } @@ -3682,6 +3685,7 @@ static int do_md_run(mddev_t * mddev) mddev->changed = 1; md_new_event(mddev); sysfs_notify(&mddev->kobj, NULL, "array_state"); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); kobject_uevent(&mddev->gendisk->dev.kobj, KOBJ_CHANGE); return 0; } @@ -4252,6 +4256,8 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) export_rdev(rdev); md_update_sb(mddev, 1); + if (mddev->degraded) + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); return err; @@ -5105,6 +5111,8 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) if (!mddev->pers->error_handler) return; mddev->pers->error_handler(mddev,rdev); + if (mddev->degraded) + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_INTR, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); @@ -6055,13 +6063,18 @@ void md_check_recovery(mddev_t *mddev) mddev->recovery = 0; /* flag recovery needed just to double check */ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); md_new_event(mddev); goto unlock; } + /* Set RUNNING before clearing NEEDED to avoid + * any transients in the value of "sync_action". + */ + set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); /* Clear some bits that don't mean anything, but * might be left set */ - clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); clear_bit(MD_RECOVERY_INTR, &mddev->recovery); clear_bit(MD_RECOVERY_DONE, &mddev->recovery); @@ -6079,17 +6092,19 @@ void md_check_recovery(mddev_t *mddev) /* Cannot proceed */ goto unlock; set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery); + clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if ((spares = remove_and_add_spares(mddev))) { clear_bit(MD_RECOVERY_SYNC, &mddev->recovery); clear_bit(MD_RECOVERY_CHECK, &mddev->recovery); + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if (mddev->recovery_cp < MaxSector) { set_bit(MD_RECOVERY_SYNC, &mddev->recovery); + clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) /* nothing to be done ... */ goto unlock; if (mddev->pers->sync_request) { - set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); if (spares && mddev->bitmap && ! mddev->bitmap->file) { /* We are adding a device or devices to an array * which has the bitmap stored on all devices. @@ -6108,9 +6123,16 @@ void md_check_recovery(mddev_t *mddev) mddev->recovery = 0; } else md_wakeup_thread(mddev->sync_thread); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); md_new_event(mddev); } unlock: + if (!mddev->sync_thread) { + clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + if (test_and_clear_bit(MD_RECOVERY_RECOVER, + &mddev->recovery)) + sysfs_notify(&mddev->kobj, NULL, "sync_action"); + } mddev_unlock(mddev); } } diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 780e0613e6d5..62aa9c9a6ddc 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -188,6 +188,7 @@ struct mddev_s * NEEDED: we might need to start a resync/recover * RUNNING: a thread is running, or about to be started * SYNC: actually doing a resync, not a recovery + * RECOVER: doing recovery, or need to try it. * INTR: resync needs to be aborted for some reason * DONE: thread is done and is waiting to be reaped * REQUEST: user-space has requested a sync (used with SYNC) @@ -198,6 +199,7 @@ struct mddev_s */ #define MD_RECOVERY_RUNNING 0 #define MD_RECOVERY_SYNC 1 +#define MD_RECOVERY_RECOVER 2 #define MD_RECOVERY_INTR 3 #define MD_RECOVERY_DONE 4 #define MD_RECOVERY_NEEDED 5 -- cgit From 526647320e696f434647f38421a6ecf65b859c43 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:44 +1000 Subject: Make sure all changes to md/dev-XX/state are notified The important state change happens during an interrupt in md_error. So just set a flag there and call sysfs_notify later in process context. Signed-off-by: Neil Brown --- Documentation/md.txt | 10 ++++++++++ drivers/md/md.c | 14 +++++++++++++- include/linux/raid/md_k.h | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/md.txt b/Documentation/md.txt index eb6e69e3732e..e06cc59437e4 100644 --- a/Documentation/md.txt +++ b/Documentation/md.txt @@ -297,6 +297,10 @@ Each directory contains: writemostly - device will only be subject to read requests if there are no other options. This applies only to raid1 arrays. + blocked - device has failed, metadata is "external", + and the failure hasn't been acknowledged yet. + Writes that would write to this device if + it were not faulty are blocked. spare - device is working, but not a full member. This includes spares that are in the process of being recovered to @@ -306,6 +310,12 @@ Each directory contains: Writing "remove" removes the device from the array. Writing "writemostly" sets the writemostly flag. Writing "-writemostly" clears the writemostly flag. + Writing "blocked" sets the "blocked" flag. + Writing "-blocked" clear the "blocked" flag and allows writes + to complete. + + This file responds to select/poll. Any change to 'faulty' + or 'blocked' causes an event. errors An approximate count of read errors that have been detected on diff --git a/drivers/md/md.c b/drivers/md/md.c index 60d4cad88c20..dc99d95a1b6d 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1886,6 +1886,8 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len) err = 0; } + if (!err) + sysfs_notify(&rdev->kobj, NULL, "state"); return err ? err : len; } static struct rdev_sysfs_entry rdev_state = @@ -1979,7 +1981,8 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) if (err) { rdev->raid_disk = -1; return err; - } + } else + sysfs_notify(&rdev->kobj, NULL, "state"); sprintf(nm, "rd%d", rdev->raid_disk); if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm)) printk(KERN_WARNING @@ -1996,6 +1999,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) clear_bit(Faulty, &rdev->flags); clear_bit(WriteMostly, &rdev->flags); set_bit(In_sync, &rdev->flags); + sysfs_notify(&rdev->kobj, NULL, "state"); } return len; } @@ -3525,6 +3529,7 @@ static int do_md_run(mddev_t * mddev) return -EINVAL; } } + sysfs_notify(&rdev->kobj, NULL, "state"); } md_probe(mddev->unit, NULL, NULL); @@ -4256,6 +4261,8 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) } if (err) export_rdev(rdev); + else + sysfs_notify(&rdev->kobj, NULL, "state"); md_update_sb(mddev, 1); if (mddev->degraded) @@ -5115,6 +5122,7 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) mddev->pers->error_handler(mddev,rdev); if (mddev->degraded) set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); + set_bit(StateChanged, &rdev->flags); set_bit(MD_RECOVERY_INTR, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); @@ -6037,6 +6045,10 @@ void md_check_recovery(mddev_t *mddev) if (mddev->flags) md_update_sb(mddev, 0); + rdev_for_each(rdev, rtmp, mddev) + if (test_and_clear_bit(StateChanged, &rdev->flags)) + sysfs_notify(&rdev->kobj, NULL, "state"); + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) && !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) { diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 62aa9c9a6ddc..df30c4395875 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -87,6 +87,9 @@ struct mdk_rdev_s #define Blocked 8 /* An error occured on an externally * managed array, don't allow writes * until it is cleared */ +#define StateChanged 9 /* Faulty or Blocked has changed during + * interrupt, so it needs to be + * notified by the thread */ wait_queue_head_t blocked_wait; int desc_nr; /* descriptor index in the superblock */ -- cgit From b203886edbcaac3ca427cf4dbcb50b18bdb346fd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:31:50 +1000 Subject: md: kill STRIPE_OP_MOD_DMA in raid5 offload From: Dan Williams This micro-optimization allowed the raid code to skip a re-read of the parity block after checking parity. It took advantage of the fact that xor-offload-engines have their own internal result buffer and can check parity without writing to memory. Remove it for the following reasons: 1/ It is a layering violation for MD to need to manage the DMA and non-DMA paths within async_xor_zero_sum 2/ Bad precedent to toggle the 'ops' flags outside the lock 3/ Hard to realize a performance gain as reads will not need an updated parity block and writes will dirty it anyways. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 10 ---------- include/linux/raid/raid5.h | 2 -- 2 files changed, 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 8c4e6149daea..60e61d2464b5 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -837,15 +837,10 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, static void ops_complete_check(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; - int pd_idx = sh->pd_idx; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - if (test_and_clear_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending) && - sh->ops.zero_sum_result == 0) - set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); - set_bit(STRIPE_OP_CHECK, &sh->ops.complete); set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); @@ -873,11 +868,6 @@ static void ops_run_check(struct stripe_head *sh) tx = async_xor_zero_sum(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, &sh->ops.zero_sum_result, 0, NULL, NULL, NULL); - if (tx) - set_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending); - else - clear_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending); - atomic_inc(&sh->count); tx = async_trigger_callback(ASYNC_TX_DEP_ACK | ASYNC_TX_ACK, tx, ops_complete_check, sh); diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index f0827d31ae6f..4ecae31a3dcb 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -267,10 +267,8 @@ struct r6_state { /* modifiers to the base operations * STRIPE_OP_MOD_REPAIR_PD - compute the parity block and write it back - * STRIPE_OP_MOD_DMA_CHECK - parity is not corrupted by the check */ #define STRIPE_OP_MOD_REPAIR_PD 7 -#define STRIPE_OP_MOD_DMA_CHECK 8 /* * Plugging: -- cgit From 2b7497f0e0a0b9cf21d822e427d5399b2056501a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:31:52 +1000 Subject: md: kill STRIPE_OP_IO flag From: Dan Williams The R5_Want{Read,Write} flags already gate i/o. So, this flag is superfluous and we can unconditionally call ops_run_io(). Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 32 +++++--------------------------- include/linux/raid/raid5.h | 1 - 2 files changed, 5 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 60e61d2464b5..cac97080b278 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -373,8 +373,6 @@ static unsigned long get_stripe_work(struct stripe_head *sh) test_and_ack_op(STRIPE_OP_BIODRAIN, pending); test_and_ack_op(STRIPE_OP_POSTXOR, pending); test_and_ack_op(STRIPE_OP_CHECK, pending); - if (test_and_clear_bit(STRIPE_OP_IO, &sh->ops.pending)) - ack++; sh->ops.count -= ack; if (unlikely(sh->ops.count < 0)) { @@ -399,7 +397,6 @@ static void ops_run_io(struct stripe_head *sh) might_sleep(); - set_bit(STRIPE_IO_STARTED, &sh->state); for (i = disks; i--; ) { int rw; struct bio *bi; @@ -433,6 +430,8 @@ static void ops_run_io(struct stripe_head *sh) test_bit(STRIPE_EXPAND_READY, &sh->state)) md_sync_acct(rdev->bdev, STRIPE_SECTORS); + set_bit(STRIPE_IO_STARTED, &sh->state); + bi->bi_bdev = rdev->bdev; pr_debug("%s: for %llu schedule op %ld on disc %d\n", __func__, (unsigned long long)sh->sector, @@ -900,9 +899,6 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) if (test_bit(STRIPE_OP_CHECK, &pending)) ops_run_check(sh); - if (test_bit(STRIPE_OP_IO, &pending)) - ops_run_io(sh); - if (overlap_clear) for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -2013,8 +2009,6 @@ static int __handle_issuing_new_read_requests5(struct stripe_head *sh, */ set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; pr_debug("Reading block %d (sync=%d)\n", disk_idx, s->syncing); @@ -2208,9 +2202,6 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, "%d for r-m-w\n", i); set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; } else { set_bit(STRIPE_DELAYED, &sh->state); @@ -2234,9 +2225,6 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, "%d for Reconstruct\n", i); set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; } else { set_bit(STRIPE_DELAYED, &sh->state); @@ -2444,8 +2432,6 @@ static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh, set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; clear_bit(STRIPE_DEGRADED, &sh->state); s->locked++; @@ -2801,9 +2787,6 @@ static void handle_stripe5(struct stripe_head *sh) (i == sh->pd_idx || dev->written)) { pr_debug("Writing block %d\n", i); set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; if (prexor) continue; if (!test_bit(R5_Insync, &dev->flags) || @@ -2857,16 +2840,12 @@ static void handle_stripe5(struct stripe_head *sh) dev = &sh->dev[s.failed_num]; if (!test_bit(R5_ReWrite, &dev->flags)) { set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; set_bit(R5_ReWrite, &dev->flags); set_bit(R5_LOCKED, &dev->flags); s.locked++; } else { /* let's read it back */ set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; set_bit(R5_LOCKED, &dev->flags); s.locked++; } @@ -2884,13 +2863,10 @@ static void handle_stripe5(struct stripe_head *sh) clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - for (i = conf->raid_disks; i--; ) { + for (i = conf->raid_disks; i--; ) set_bit(R5_Wantwrite, &sh->dev[i].flags); set_bit(R5_LOCKED, &dev->flags); s.locked++; - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; - } } if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) && @@ -2926,6 +2902,8 @@ static void handle_stripe5(struct stripe_head *sh) if (pending) raid5_run_ops(sh, pending); + ops_run_io(sh); + return_io(return_bi); } diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 4ecae31a3dcb..1301195abf4b 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -263,7 +263,6 @@ struct r6_state { #define STRIPE_OP_BIODRAIN 3 #define STRIPE_OP_POSTXOR 4 #define STRIPE_OP_CHECK 5 -#define STRIPE_OP_IO 6 /* modifiers to the base operations * STRIPE_OP_MOD_REPAIR_PD - compute the parity block and write it back -- cgit From ecc65c9b3f9b9d740a5deade3d85b39be56401b6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:31:57 +1000 Subject: md: replace STRIPE_OP_CHECK with 'check_states' From: Dan Williams The STRIPE_OP_* flags record the state of stripe operations which are performed outside the stripe lock. Their use in indicating which operations need to be run is straightforward; however, interpolating what the next state of the stripe should be based on a given combination of these flags is not straightforward, and has led to bugs. An easier to read implementation with minimal degrees of freedom is needed. Towards this goal, this patch introduces explicit states to replace what was previously interpolated from the STRIPE_OP_* flags. For now this only converts the handle_parity_checks5 path, removing a user of the ops.{pending,ack,complete,count} fields of struct stripe_operations. This conversion also found a remaining issue with the current code. There is a small window for a drive to fail between when we schedule a repair and when the parity calculation for that repair completes. When this happens we will writeback to 'failed_num' when we really want to write back to 'pd_idx'. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 172 ++++++++++++++++++++++----------------------- include/linux/raid/raid5.h | 46 ++++++++++-- 2 files changed, 123 insertions(+), 95 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 6f3dd12dd3a4..544e1600f208 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -605,7 +605,11 @@ static void ops_complete_compute5(void *stripe_head_ref) set_bit(R5_UPTODATE, &tgt->flags); BUG_ON(!test_bit(R5_Wantcompute, &tgt->flags)); clear_bit(R5_Wantcompute, &tgt->flags); - set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); + clear_bit(STRIPE_COMPUTE_RUN, &sh->state); + if (sh->check_state == check_state_compute_run) + sh->check_state = check_state_compute_result; + else + set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } @@ -838,7 +842,7 @@ static void ops_complete_check(void *stripe_head_ref) pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - set_bit(STRIPE_OP_CHECK, &sh->ops.complete); + sh->check_state = check_state_check_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } @@ -870,7 +874,8 @@ static void ops_run_check(struct stripe_head *sh) ops_complete_check, sh); } -static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) +static void raid5_run_ops(struct stripe_head *sh, unsigned long pending, + unsigned long ops_request) { int overlap_clear = 0, i, disks = sh->disks; struct dma_async_tx_descriptor *tx = NULL; @@ -880,7 +885,8 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) overlap_clear++; } - if (test_bit(STRIPE_OP_COMPUTE_BLK, &pending)) + if (test_bit(STRIPE_OP_COMPUTE_BLK, &pending) || + test_bit(STRIPE_OP_COMPUTE_BLK, &ops_request)) tx = ops_run_compute5(sh, pending); if (test_bit(STRIPE_OP_PREXOR, &pending)) @@ -894,7 +900,7 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) if (test_bit(STRIPE_OP_POSTXOR, &pending)) ops_run_postxor(sh, tx, pending); - if (test_bit(STRIPE_OP_CHECK, &pending)) + if (test_bit(STRIPE_OP_CHECK, &ops_request)) ops_run_check(sh); if (overlap_clear) @@ -1961,8 +1967,7 @@ static int __handle_issuing_new_read_requests5(struct stripe_head *sh, /* don't schedule compute operations or reads on the parity block while * a check is in flight */ - if ((disk_idx == sh->pd_idx) && - test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) + if (disk_idx == sh->pd_idx && sh->check_state) return ~0; /* is the data in this block needed, and can we get it? */ @@ -1983,9 +1988,8 @@ static int __handle_issuing_new_read_requests5(struct stripe_head *sh, * 3/ We hold off parity block re-reads until check operations * have quiesced. */ - if ((s->uptodate == disks - 1) && - (s->failed && disk_idx == s->failed_num) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) { + if ((s->uptodate == disks - 1) && !sh->check_state && + (s->failed && disk_idx == s->failed_num)) { set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); set_bit(R5_Wantcompute, &dev->flags); sh->ops.target = disk_idx; @@ -2021,12 +2025,8 @@ static void handle_issuing_new_read_requests5(struct stripe_head *sh, { int i; - /* Clear completed compute operations. Parity recovery - * (STRIPE_OP_MOD_REPAIR_PD) implies a write-back which is handled - * later on in this routine - */ - if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete) && - !test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { + /* Clear completed compute operations */ + if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete)) { clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.ack); clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); @@ -2350,90 +2350,85 @@ static void handle_issuing_new_write_requests6(raid5_conf_t *conf, static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh, struct stripe_head_state *s, int disks) { - int canceled_check = 0; + struct r5dev *dev = NULL; set_bit(STRIPE_HANDLE, &sh->state); - /* complete a check operation */ - if (test_and_clear_bit(STRIPE_OP_CHECK, &sh->ops.complete)) { - clear_bit(STRIPE_OP_CHECK, &sh->ops.ack); - clear_bit(STRIPE_OP_CHECK, &sh->ops.pending); + switch (sh->check_state) { + case check_state_idle: + /* start a new check operation if there are no failures */ if (s->failed == 0) { - if (sh->ops.zero_sum_result == 0) - /* parity is correct (on disc, - * not in buffer any more) - */ - set_bit(STRIPE_INSYNC, &sh->state); - else { - conf->mddev->resync_mismatches += - STRIPE_SECTORS; - if (test_bit( - MD_RECOVERY_CHECK, &conf->mddev->recovery)) - /* don't try to repair!! */ - set_bit(STRIPE_INSYNC, &sh->state); - else { - set_bit(STRIPE_OP_COMPUTE_BLK, - &sh->ops.pending); - set_bit(STRIPE_OP_MOD_REPAIR_PD, - &sh->ops.pending); - set_bit(R5_Wantcompute, - &sh->dev[sh->pd_idx].flags); - sh->ops.target = sh->pd_idx; - sh->ops.count++; - s->uptodate++; - } - } - } else - canceled_check = 1; /* STRIPE_INSYNC is not set */ - } - - /* start a new check operation if there are no failures, the stripe is - * not insync, and a repair is not in flight - */ - if (s->failed == 0 && - !test_bit(STRIPE_INSYNC, &sh->state) && - !test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { - if (!test_and_set_bit(STRIPE_OP_CHECK, &sh->ops.pending)) { BUG_ON(s->uptodate != disks); + sh->check_state = check_state_run; + set_bit(STRIPE_OP_CHECK, &s->ops_request); clear_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags); - sh->ops.count++; s->uptodate--; + break; } - } - - /* check if we can clear a parity disk reconstruct */ - if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete) && - test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { - - clear_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.ack); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); - } - + dev = &sh->dev[s->failed_num]; + /* fall through */ + case check_state_compute_result: + sh->check_state = check_state_idle; + if (!dev) + dev = &sh->dev[sh->pd_idx]; + + /* check that a write has not made the stripe insync */ + if (test_bit(STRIPE_INSYNC, &sh->state)) + break; - /* Wait for check parity and compute block operations to complete - * before write-back. If a failure occurred while the check operation - * was in flight we need to cycle this stripe through handle_stripe - * since the parity block may not be uptodate - */ - if (!canceled_check && !test_bit(STRIPE_INSYNC, &sh->state) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending) && - !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) { - struct r5dev *dev; /* either failed parity check, or recovery is happening */ - if (s->failed == 0) - s->failed_num = sh->pd_idx; - dev = &sh->dev[s->failed_num]; BUG_ON(!test_bit(R5_UPTODATE, &dev->flags)); BUG_ON(s->uptodate != disks); set_bit(R5_LOCKED, &dev->flags); + s->locked++; set_bit(R5_Wantwrite, &dev->flags); clear_bit(STRIPE_DEGRADED, &sh->state); - s->locked++; set_bit(STRIPE_INSYNC, &sh->state); + break; + case check_state_run: + break; /* we will be called again upon completion */ + case check_state_check_result: + sh->check_state = check_state_idle; + + /* if a failure occurred during the check operation, leave + * STRIPE_INSYNC not set and let the stripe be handled again + */ + if (s->failed) + break; + + /* handle a successful check operation, if parity is correct + * we are done. Otherwise update the mismatch count and repair + * parity if !MD_RECOVERY_CHECK + */ + if (sh->ops.zero_sum_result == 0) + /* parity is correct (on disc, + * not in buffer any more) + */ + set_bit(STRIPE_INSYNC, &sh->state); + else { + conf->mddev->resync_mismatches += STRIPE_SECTORS; + if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery)) + /* don't try to repair!! */ + set_bit(STRIPE_INSYNC, &sh->state); + else { + sh->check_state = check_state_compute_run; + set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request); + set_bit(R5_Wantcompute, + &sh->dev[sh->pd_idx].flags); + sh->ops.target = sh->pd_idx; + s->uptodate++; + } + } + break; + case check_state_compute_run: + break; + default: + printk(KERN_ERR "%s: unknown check_state: %d sector: %llu\n", + __func__, sh->check_state, + (unsigned long long) sh->sector); + BUG(); } } @@ -2807,7 +2802,7 @@ static void handle_stripe5(struct stripe_head *sh) * block. */ if (s.to_write && !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) + !sh->check_state) handle_issuing_new_write_requests5(conf, sh, &s, disks); /* maybe we need to check and possibly fix the parity for this stripe @@ -2815,11 +2810,10 @@ static void handle_stripe5(struct stripe_head *sh) * data is available. The parity check is held off while parity * dependent operations are in flight. */ - if ((s.syncing && s.locked == 0 && + if (sh->check_state || + (s.syncing && s.locked == 0 && !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending) && - !test_bit(STRIPE_INSYNC, &sh->state)) || - test_bit(STRIPE_OP_CHECK, &sh->ops.pending) || - test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) + !test_bit(STRIPE_INSYNC, &sh->state))) handle_parity_checks5(conf, sh, &s, disks); if (s.syncing && s.locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) { @@ -2897,8 +2891,8 @@ static void handle_stripe5(struct stripe_head *sh) if (unlikely(blocked_rdev)) md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); - if (pending) - raid5_run_ops(sh, pending); + if (pending || s.ops_request) + raid5_run_ops(sh, pending, s.ops_request); ops_run_io(sh, &s); diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 1301195abf4b..2c96d5fd54bf 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -158,6 +158,41 @@ * the compute block completes. */ +/* + * Operations state - intermediate states that are visible outside of sh->lock + * In general _idle indicates nothing is running, _run indicates a data + * processing operation is active, and _result means the data processing result + * is stable and can be acted upon. For simple operations like biofill and + * compute that only have an _idle and _run state they are indicated with + * sh->state flags (STRIPE_BIOFILL_RUN and STRIPE_COMPUTE_RUN) + */ +/** + * enum check_states - handles syncing / repairing a stripe + * @check_state_idle - check operations are quiesced + * @check_state_run - check operation is running + * @check_state_result - set outside lock when check result is valid + * @check_state_compute_run - check failed and we are repairing + * @check_state_compute_result - set outside lock when compute result is valid + */ +enum check_states { + check_state_idle = 0, + check_state_run, /* parity check */ + check_state_check_result, + check_state_compute_run, /* parity repair */ + check_state_compute_result, +}; + +/** + * enum reconstruct_states - handles writing or expanding a stripe + */ +enum reconstruct_states { + reconstruct_state_idle = 0, + reconstruct_state_drain_run, /* write */ + reconstruct_state_run, /* expand */ + reconstruct_state_drain_result, + reconstruct_state_result, +}; + struct stripe_head { struct hlist_node hash; struct list_head lru; /* inactive_list or handle_list */ @@ -169,6 +204,7 @@ struct stripe_head { spinlock_t lock; int bm_seq; /* sequence number for bitmap flushes */ int disks; /* disks in stripe */ + enum check_states check_state; /* stripe_operations * @pending - pending ops flags (set for request->issue->complete) * @ack - submitted ops flags (set for issue->complete) @@ -202,6 +238,7 @@ struct stripe_head_state { int locked, uptodate, to_read, to_write, failed, written; int to_fill, compute, req_compute, non_overwrite; int failed_num; + unsigned long ops_request; }; /* r6_state - extra state data only relevant to r6 */ @@ -254,8 +291,10 @@ struct r6_state { #define STRIPE_EXPAND_READY 11 #define STRIPE_IO_STARTED 12 /* do not count towards 'bypass_count' */ #define STRIPE_FULL_WRITE 13 /* all blocks are set to be overwritten */ +#define STRIPE_BIOFILL_RUN 14 +#define STRIPE_COMPUTE_RUN 15 /* - * Operations flags (in issue order) + * Operation request flags */ #define STRIPE_OP_BIOFILL 0 #define STRIPE_OP_COMPUTE_BLK 1 @@ -264,11 +303,6 @@ struct r6_state { #define STRIPE_OP_POSTXOR 4 #define STRIPE_OP_CHECK 5 -/* modifiers to the base operations - * STRIPE_OP_MOD_REPAIR_PD - compute the parity block and write it back - */ -#define STRIPE_OP_MOD_REPAIR_PD 7 - /* * Plugging: * -- cgit From 600aa10993012ff2dd5617720dac081e4f992017 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:32:05 +1000 Subject: md: replace STRIPE_OP_{BIODRAIN,PREXOR,POSTXOR} with 'reconstruct_states' From: Dan Williams Track the state of reconstruct operations (recalculating the parity block usually due to incoming writes, or as part of array expansion) Reduces the scope of the STRIPE_OP_{BIODRAIN,PREXOR,POSTXOR} flags to only tracking whether a reconstruct operation has been requested via the ops_request field of struct stripe_head_state. This is the final step in the removal of ops.{pending,ack,complete,count}, i.e. the STRIPE_OP_{BIODRAIN,PREXOR,POSTXOR} flags only request an operation and do not track the state of the operation. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 204 ++++++++++++++------------------------------- include/linux/raid/raid5.h | 9 +- 2 files changed, 63 insertions(+), 150 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 835046bf384e..b9159367491a 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -122,6 +122,13 @@ static void return_io(struct bio *return_bi) static void print_raid5_conf (raid5_conf_t *conf); +static int stripe_operations_active(struct stripe_head *sh) +{ + return sh->check_state || sh->reconstruct_state || + test_bit(STRIPE_BIOFILL_RUN, &sh->state) || + test_bit(STRIPE_COMPUTE_RUN, &sh->state); +} + static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) { if (atomic_dec_and_test(&sh->count)) { @@ -141,7 +148,7 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) } md_wakeup_thread(conf->mddev->thread); } else { - BUG_ON(sh->ops.pending); + BUG_ON(stripe_operations_active(sh)); if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { atomic_dec(&conf->preread_active_stripes); if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) @@ -243,7 +250,7 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx, int BUG_ON(atomic_read(&sh->count) != 0); BUG_ON(test_bit(STRIPE_HANDLE, &sh->state)); - BUG_ON(sh->ops.pending || sh->ops.ack || sh->ops.complete); + BUG_ON(stripe_operations_active(sh)); CHECK_DEVLOCK(); pr_debug("init_stripe called, stripe %llu\n", @@ -344,47 +351,6 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector return sh; } -/* test_and_ack_op() ensures that we only dequeue an operation once */ -#define test_and_ack_op(op, pend) \ -do { \ - if (test_bit(op, &sh->ops.pending) && \ - !test_bit(op, &sh->ops.complete)) { \ - if (test_and_set_bit(op, &sh->ops.ack)) \ - clear_bit(op, &pend); \ - else \ - ack++; \ - } else \ - clear_bit(op, &pend); \ -} while (0) - -/* find new work to run, do not resubmit work that is already - * in flight - */ -static unsigned long get_stripe_work(struct stripe_head *sh) -{ - unsigned long pending; - int ack = 0; - - pending = sh->ops.pending; - - test_and_ack_op(STRIPE_OP_BIOFILL, pending); - test_and_ack_op(STRIPE_OP_COMPUTE_BLK, pending); - test_and_ack_op(STRIPE_OP_PREXOR, pending); - test_and_ack_op(STRIPE_OP_BIODRAIN, pending); - test_and_ack_op(STRIPE_OP_POSTXOR, pending); - test_and_ack_op(STRIPE_OP_CHECK, pending); - - sh->ops.count -= ack; - if (unlikely(sh->ops.count < 0)) { - printk(KERN_ERR "pending: %#lx ops.pending: %#lx ops.ack: %#lx " - "ops.complete: %#lx\n", pending, sh->ops.pending, - sh->ops.ack, sh->ops.complete); - BUG(); - } - - return pending; -} - static void raid5_end_read_request(struct bio *bi, int error); static void @@ -609,7 +575,7 @@ static void ops_complete_compute5(void *stripe_head_ref) } static struct dma_async_tx_descriptor * -ops_run_compute5(struct stripe_head *sh, unsigned long pending) +ops_run_compute5(struct stripe_head *sh, unsigned long ops_request) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -640,7 +606,7 @@ ops_run_compute5(struct stripe_head *sh, unsigned long pending) ops_complete_compute5, sh); /* ack now if postxor is not set to be run */ - if (tx && !test_bit(STRIPE_OP_POSTXOR, &pending)) + if (tx && !test_bit(STRIPE_OP_POSTXOR, &ops_request)) async_tx_ack(tx); return tx; @@ -652,8 +618,6 @@ static void ops_complete_prexor(void *stripe_head_ref) pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - - set_bit(STRIPE_OP_PREXOR, &sh->ops.complete); } static struct dma_async_tx_descriptor * @@ -686,7 +650,7 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) static struct dma_async_tx_descriptor * ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long pending) + unsigned long ops_request) { int disks = sh->disks; int pd_idx = sh->pd_idx, i; @@ -694,7 +658,7 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, /* check if prexor is active which means only process blocks * that are part of a read-modify-write (Wantprexor) */ - int prexor = test_bit(STRIPE_OP_PREXOR, &pending); + int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -744,7 +708,7 @@ static void ops_complete_postxor(void *stripe_head_ref) pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); + sh->reconstruct_state = reconstruct_state_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } @@ -763,16 +727,14 @@ static void ops_complete_write(void *stripe_head_ref) set_bit(R5_UPTODATE, &dev->flags); } - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - + sh->reconstruct_state = reconstruct_state_drain_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } static void ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long pending) + unsigned long ops_request) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -780,7 +742,7 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, int count = 0, pd_idx = sh->pd_idx, i; struct page *xor_dest; - int prexor = test_bit(STRIPE_OP_PREXOR, &pending); + int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); unsigned long flags; dma_async_tx_callback callback; @@ -807,7 +769,7 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, } /* check whether this postxor is part of a write */ - callback = test_bit(STRIPE_OP_BIODRAIN, &pending) ? + callback = test_bit(STRIPE_OP_BIODRAIN, &ops_request) ? ops_complete_write : ops_complete_postxor; /* 1/ if we prexor'd then the dest is reused as a source @@ -868,8 +830,7 @@ static void ops_run_check(struct stripe_head *sh) ops_complete_check, sh); } -static void raid5_run_ops(struct stripe_head *sh, unsigned long pending, - unsigned long ops_request) +static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) { int overlap_clear = 0, i, disks = sh->disks; struct dma_async_tx_descriptor *tx = NULL; @@ -880,18 +841,18 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending, } if (test_bit(STRIPE_OP_COMPUTE_BLK, &ops_request)) - tx = ops_run_compute5(sh, pending); + tx = ops_run_compute5(sh, ops_request); - if (test_bit(STRIPE_OP_PREXOR, &pending)) + if (test_bit(STRIPE_OP_PREXOR, &ops_request)) tx = ops_run_prexor(sh, tx); - if (test_bit(STRIPE_OP_BIODRAIN, &pending)) { - tx = ops_run_biodrain(sh, tx, pending); + if (test_bit(STRIPE_OP_BIODRAIN, &ops_request)) { + tx = ops_run_biodrain(sh, tx, ops_request); overlap_clear++; } - if (test_bit(STRIPE_OP_POSTXOR, &pending)) - ops_run_postxor(sh, tx, pending); + if (test_bit(STRIPE_OP_POSTXOR, &ops_request)) + ops_run_postxor(sh, tx, ops_request); if (test_bit(STRIPE_OP_CHECK, &ops_request)) ops_run_check(sh); @@ -1684,11 +1645,11 @@ static void compute_block_2(struct stripe_head *sh, int dd_idx1, int dd_idx2) } } -static int -handle_write_operations5(struct stripe_head *sh, int rcw, int expand) +static void +handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, + int rcw, int expand) { int i, pd_idx = sh->pd_idx, disks = sh->disks; - int locked = 0; if (rcw) { /* if we are not expanding this is a proper write request, and @@ -1696,12 +1657,12 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) * stripe cache */ if (!expand) { - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - sh->ops.count++; - } + sh->reconstruct_state = reconstruct_state_drain_run; + set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); + } else + sh->reconstruct_state = reconstruct_state_run; - set_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - sh->ops.count++; + set_bit(STRIPE_OP_POSTXOR, &s->ops_request); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -1710,21 +1671,20 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) set_bit(R5_LOCKED, &dev->flags); if (!expand) clear_bit(R5_UPTODATE, &dev->flags); - locked++; + s->locked++; } } - if (locked + 1 == disks) + if (s->locked + 1 == disks) if (!test_and_set_bit(STRIPE_FULL_WRITE, &sh->state)) atomic_inc(&sh->raid_conf->pending_full_writes); } else { BUG_ON(!(test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags) || test_bit(R5_Wantcompute, &sh->dev[pd_idx].flags))); - set_bit(STRIPE_OP_PREXOR, &sh->ops.pending); - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - - sh->ops.count += 3; + sh->reconstruct_state = reconstruct_state_drain_run; + set_bit(STRIPE_OP_PREXOR, &s->ops_request); + set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); + set_bit(STRIPE_OP_POSTXOR, &s->ops_request); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -1742,7 +1702,7 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) set_bit(R5_Wantprexor, &dev->flags); set_bit(R5_LOCKED, &dev->flags); clear_bit(R5_UPTODATE, &dev->flags); - locked++; + s->locked++; } } } @@ -1752,13 +1712,11 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) */ set_bit(R5_LOCKED, &sh->dev[pd_idx].flags); clear_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); - locked++; + s->locked++; - pr_debug("%s: stripe %llu locked: %d pending: %lx\n", + pr_debug("%s: stripe %llu locked: %d ops_request: %lx\n", __func__, (unsigned long long)sh->sector, - locked, sh->ops.pending); - - return locked; + s->locked, s->ops_request); } /* @@ -2005,8 +1963,7 @@ static void handle_issuing_new_read_requests5(struct stripe_head *sh, * midst of changing due to a write */ if (!test_bit(STRIPE_COMPUTE_RUN, &sh->state) && !sh->check_state && - !test_bit(STRIPE_OP_PREXOR, &sh->ops.pending) && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + !sh->reconstruct_state) { for (i = disks; i--; ) if (__handle_issuing_new_read_requests5( sh, s, i, disks) == 0) @@ -2211,7 +2168,7 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, if ((s->req_compute || !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) && (s->locked == 0 && (rcw == 0 || rmw == 0) && !test_bit(STRIPE_BIT_DELAY, &sh->state))) - s->locked += handle_write_operations5(sh, rcw == 0, 0); + handle_write_operations5(sh, s, rcw == 0, 0); } static void handle_issuing_new_write_requests6(raid5_conf_t *conf, @@ -2581,15 +2538,14 @@ static void handle_stripe5(struct stripe_head *sh) struct bio *return_bi = NULL; struct stripe_head_state s; struct r5dev *dev; - unsigned long pending = 0; mdk_rdev_t *blocked_rdev = NULL; int prexor; memset(&s, 0, sizeof(s)); - pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d " - "ops=%lx:%lx:%lx\n", (unsigned long long)sh->sector, sh->state, - atomic_read(&sh->count), sh->pd_idx, - sh->ops.pending, sh->ops.ack, sh->ops.complete); + pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d check:%d " + "reconstruct:%d\n", (unsigned long long)sh->sector, sh->state, + atomic_read(&sh->count), sh->pd_idx, sh->check_state, + sh->reconstruct_state); spin_lock(&sh->lock); clear_bit(STRIPE_HANDLE, &sh->state); @@ -2703,34 +2659,12 @@ static void handle_stripe5(struct stripe_head *sh) /* Now we check to see if any write operations have recently * completed */ - - /* leave prexor set until postxor is done, allows us to distinguish - * a rmw from a rcw during biodrain - */ prexor = 0; - if (test_bit(STRIPE_OP_PREXOR, &sh->ops.complete) && - test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete)) { - - prexor = 1; - clear_bit(STRIPE_OP_PREXOR, &sh->ops.complete); - clear_bit(STRIPE_OP_PREXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_PREXOR, &sh->ops.pending); - + if (sh->reconstruct_state == reconstruct_state_drain_result) { + sh->reconstruct_state = reconstruct_state_idle; for (i = disks; i--; ) - clear_bit(R5_Wantprexor, &sh->dev[i].flags); - } - - /* if only POSTXOR is set then this is an 'expand' postxor */ - if (test_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete) && - test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete)) { - - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete); - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.ack); - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); + prexor += test_and_clear_bit(R5_Wantprexor, + &sh->dev[i].flags); /* All the 'written' buffers and the parity block are ready to * be written back to disk @@ -2763,8 +2697,7 @@ static void handle_stripe5(struct stripe_head *sh) * 2/ A 'check' operation is in flight, as it may clobber the parity * block. */ - if (s.to_write && !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending) && - !sh->check_state) + if (s.to_write && !sh->reconstruct_state && !sh->check_state) handle_issuing_new_write_requests5(conf, sh, &s, disks); /* maybe we need to check and possibly fix the parity for this stripe @@ -2805,18 +2738,10 @@ static void handle_stripe5(struct stripe_head *sh) } } - /* Finish postxor operations initiated by the expansion - * process - */ - if (test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete) && - !test_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending)) { - + /* Finish reconstruct operations initiated by the expansion process */ + if (sh->reconstruct_state == reconstruct_state_result) { + sh->reconstruct_state = reconstruct_state_idle; clear_bit(STRIPE_EXPANDING, &sh->state); - - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - for (i = conf->raid_disks; i--; ) set_bit(R5_Wantwrite, &sh->dev[i].flags); set_bit(R5_LOCKED, &dev->flags); @@ -2824,15 +2749,13 @@ static void handle_stripe5(struct stripe_head *sh) } if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + !sh->reconstruct_state) { /* Need to write out all blocks after computing parity */ sh->disks = conf->raid_disks; sh->pd_idx = stripe_to_pdidx(sh->sector, conf, conf->raid_disks); - s.locked += handle_write_operations5(sh, 1, 1); - } else if (s.expanded && - s.locked == 0 && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + handle_write_operations5(sh, &s, 1, 1); + } else if (s.expanded && !sh->reconstruct_state && s.locked == 0) { clear_bit(STRIPE_EXPAND_READY, &sh->state); atomic_dec(&conf->reshape_stripes); wake_up(&conf->wait_for_overlap); @@ -2843,9 +2766,6 @@ static void handle_stripe5(struct stripe_head *sh) !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) handle_stripe_expansion(conf, sh, NULL); - if (sh->ops.count) - pending = get_stripe_work(sh); - unlock: spin_unlock(&sh->lock); @@ -2853,8 +2773,8 @@ static void handle_stripe5(struct stripe_head *sh) if (unlikely(blocked_rdev)) md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); - if (pending || s.ops_request) - raid5_run_ops(sh, pending, s.ops_request); + if (s.ops_request) + raid5_run_ops(sh, s.ops_request); ops_run_io(sh, &s); diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 2c96d5fd54bf..5f3e674b87dd 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -205,19 +205,12 @@ struct stripe_head { int bm_seq; /* sequence number for bitmap flushes */ int disks; /* disks in stripe */ enum check_states check_state; + enum reconstruct_states reconstruct_state; /* stripe_operations - * @pending - pending ops flags (set for request->issue->complete) - * @ack - submitted ops flags (set for issue->complete) - * @complete - completed ops flags (set for complete) * @target - STRIPE_OP_COMPUTE_BLK target - * @count - raid5_runs_ops is set to run when this is non-zero */ struct stripe_operations { - unsigned long pending; - unsigned long ack; - unsigned long complete; int target; - int count; u32 zero_sum_result; } ops; struct r5dev { -- cgit From d8ee0728b5b30d7a6f62c399a95e953616d31f23 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:32:06 +1000 Subject: md: replace R5_WantPrexor with R5_WantDrain, add 'prexor' reconstruct_states From: Dan Williams Currently ops_run_biodrain and other locations have extra logic to determine which blocks are processed in the prexor and non-prexor cases. This can be eliminated if handle_write_operations5 flags the blocks to be processed in all cases via R5_Wantdrain. The presence of the prexor operation is tracked in sh->reconstruct_state. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 89 +++++++++++++++------------------------------- include/linux/raid/raid5.h | 6 ++-- 2 files changed, 32 insertions(+), 63 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index b9159367491a..c71246061c0e 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -637,7 +637,7 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; /* Only process blocks that are known to be uptodate */ - if (dev->towrite && test_bit(R5_Wantprexor, &dev->flags)) + if (test_bit(R5_Wantdrain, &dev->flags)) xor_srcs[count++] = dev->page; } @@ -649,16 +649,10 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) } static struct dma_async_tx_descriptor * -ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long ops_request) +ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) { int disks = sh->disks; - int pd_idx = sh->pd_idx, i; - - /* check if prexor is active which means only process blocks - * that are part of a read-modify-write (Wantprexor) - */ - int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); + int i; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -666,20 +660,8 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; struct bio *chosen; - int towrite; - towrite = 0; - if (prexor) { /* rmw */ - if (dev->towrite && - test_bit(R5_Wantprexor, &dev->flags)) - towrite = 1; - } else { /* rcw */ - if (i != pd_idx && dev->towrite && - test_bit(R5_LOCKED, &dev->flags)) - towrite = 1; - } - - if (towrite) { + if (test_and_clear_bit(R5_Wantdrain, &dev->flags)) { struct bio *wbi; spin_lock(&sh->lock); @@ -702,18 +684,6 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, } static void ops_complete_postxor(void *stripe_head_ref) -{ - struct stripe_head *sh = stripe_head_ref; - - pr_debug("%s: stripe %llu\n", __func__, - (unsigned long long)sh->sector); - - sh->reconstruct_state = reconstruct_state_result; - set_bit(STRIPE_HANDLE, &sh->state); - release_stripe(sh); -} - -static void ops_complete_write(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; int disks = sh->disks, i, pd_idx = sh->pd_idx; @@ -727,14 +697,21 @@ static void ops_complete_write(void *stripe_head_ref) set_bit(R5_UPTODATE, &dev->flags); } - sh->reconstruct_state = reconstruct_state_drain_result; + if (sh->reconstruct_state == reconstruct_state_drain_run) + sh->reconstruct_state = reconstruct_state_drain_result; + else if (sh->reconstruct_state == reconstruct_state_prexor_drain_run) + sh->reconstruct_state = reconstruct_state_prexor_drain_result; + else { + BUG_ON(sh->reconstruct_state != reconstruct_state_run); + sh->reconstruct_state = reconstruct_state_result; + } + set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } static void -ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long ops_request) +ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -742,9 +719,8 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, int count = 0, pd_idx = sh->pd_idx, i; struct page *xor_dest; - int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); + int prexor = 0; unsigned long flags; - dma_async_tx_callback callback; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -752,7 +728,8 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, /* check if prexor is active which means only process blocks * that are part of a read-modify-write (written) */ - if (prexor) { + if (sh->reconstruct_state == reconstruct_state_prexor_drain_run) { + prexor = 1; xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page; for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -768,10 +745,6 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, } } - /* check whether this postxor is part of a write */ - callback = test_bit(STRIPE_OP_BIODRAIN, &ops_request) ? - ops_complete_write : ops_complete_postxor; - /* 1/ if we prexor'd then the dest is reused as a source * 2/ if we did not prexor then we are redoing the parity * set ASYNC_TX_XOR_DROP_DST and ASYNC_TX_XOR_ZERO_DST @@ -785,10 +758,10 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, if (unlikely(count == 1)) { flags &= ~(ASYNC_TX_XOR_DROP_DST | ASYNC_TX_XOR_ZERO_DST); tx = async_memcpy(xor_dest, xor_srcs[0], 0, 0, STRIPE_SIZE, - flags, tx, callback, sh); + flags, tx, ops_complete_postxor, sh); } else tx = async_xor(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, - flags, tx, callback, sh); + flags, tx, ops_complete_postxor, sh); } static void ops_complete_check(void *stripe_head_ref) @@ -847,12 +820,12 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) tx = ops_run_prexor(sh, tx); if (test_bit(STRIPE_OP_BIODRAIN, &ops_request)) { - tx = ops_run_biodrain(sh, tx, ops_request); + tx = ops_run_biodrain(sh, tx); overlap_clear++; } if (test_bit(STRIPE_OP_POSTXOR, &ops_request)) - ops_run_postxor(sh, tx, ops_request); + ops_run_postxor(sh, tx); if (test_bit(STRIPE_OP_CHECK, &ops_request)) ops_run_check(sh); @@ -1669,6 +1642,7 @@ handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, if (dev->towrite) { set_bit(R5_LOCKED, &dev->flags); + set_bit(R5_Wantdrain, &dev->flags); if (!expand) clear_bit(R5_UPTODATE, &dev->flags); s->locked++; @@ -1681,7 +1655,7 @@ handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, BUG_ON(!(test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags) || test_bit(R5_Wantcompute, &sh->dev[pd_idx].flags))); - sh->reconstruct_state = reconstruct_state_drain_run; + sh->reconstruct_state = reconstruct_state_prexor_drain_run; set_bit(STRIPE_OP_PREXOR, &s->ops_request); set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); set_bit(STRIPE_OP_POSTXOR, &s->ops_request); @@ -1691,15 +1665,10 @@ handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, if (i == pd_idx) continue; - /* For a read-modify write there may be blocks that are - * locked for reading while others are ready to be - * written so we distinguish these blocks by the - * R5_Wantprexor bit - */ if (dev->towrite && (test_bit(R5_UPTODATE, &dev->flags) || - test_bit(R5_Wantcompute, &dev->flags))) { - set_bit(R5_Wantprexor, &dev->flags); + test_bit(R5_Wantcompute, &dev->flags))) { + set_bit(R5_Wantdrain, &dev->flags); set_bit(R5_LOCKED, &dev->flags); clear_bit(R5_UPTODATE, &dev->flags); s->locked++; @@ -2660,11 +2629,11 @@ static void handle_stripe5(struct stripe_head *sh) * completed */ prexor = 0; - if (sh->reconstruct_state == reconstruct_state_drain_result) { + if (sh->reconstruct_state == reconstruct_state_prexor_drain_result) + prexor = 1; + if (sh->reconstruct_state == reconstruct_state_drain_result || + sh->reconstruct_state == reconstruct_state_prexor_drain_result) { sh->reconstruct_state = reconstruct_state_idle; - for (i = disks; i--; ) - prexor += test_and_clear_bit(R5_Wantprexor, - &sh->dev[i].flags); /* All the 'written' buffers and the parity block are ready to * be written back to disk diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 5f3e674b87dd..3b2672792457 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -187,8 +187,10 @@ enum check_states { */ enum reconstruct_states { reconstruct_state_idle = 0, + reconstruct_state_prexor_drain_run, /* prexor-write */ reconstruct_state_drain_run, /* write */ reconstruct_state_run, /* expand */ + reconstruct_state_prexor_drain_result, reconstruct_state_drain_result, reconstruct_state_result, }; @@ -258,9 +260,7 @@ struct r6_state { #define R5_Wantfill 12 /* dev->toread contains a bio that needs * filling */ -#define R5_Wantprexor 13 /* distinguish blocks ready for rmw from - * other "towrites" - */ +#define R5_Wantdrain 13 /* dev->towrite needs to be drained */ /* * Write method */ -- cgit From b5470dc5fc18a8ff6517c3bb538d1479e58ecb02 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 27 Jun 2008 21:44:04 -0700 Subject: md: resolve external metadata handling deadlock in md_allow_write md_allow_write() marks the metadata dirty while holding mddev->lock and then waits for the write to complete. For externally managed metadata this causes a deadlock as userspace needs to take the lock to communicate that the metadata update has completed. Change md_allow_write() in the 'external' case to start the 'mark active' operation and then return -EAGAIN. The expected side effects while waiting for userspace to write 'active' to 'array_state' are holding off reshape (code currently handles -ENOMEM), cause some 'stripe_cache_size' change requests to fail, cause some GET_BITMAP_FILE ioctl requests to fall back to GFP_NOIO, and cause updates to 'raid_disks' to fail. Except for 'stripe_cache_size' changes these failures can be mitigated by coordinating with mdmon. md_write_start() still prevents writes from occurring until the metadata handler has had a chance to take action as it unconditionally waits for MD_CHANGE_CLEAN to be cleared. [neilb@suse.de: return -EAGAIN, try GFP_NOIO] Signed-off-by: Dan Williams --- drivers/md/md.c | 27 ++++++++++++++++----------- drivers/md/raid1.c | 6 ++++-- drivers/md/raid5.c | 12 +++++++++--- include/linux/raid/md.h | 2 +- 4 files changed, 30 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index df1230af02cd..43d033d9a05a 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -4172,9 +4172,11 @@ static int get_bitmap_file(mddev_t * mddev, void __user * arg) char *ptr, *buf = NULL; int err = -ENOMEM; - md_allow_write(mddev); + if (md_allow_write(mddev)) + file = kmalloc(sizeof(*file), GFP_NOIO); + else + file = kmalloc(sizeof(*file), GFP_KERNEL); - file = kmalloc(sizeof(*file), GFP_KERNEL); if (!file) goto out; @@ -5667,15 +5669,18 @@ void md_write_end(mddev_t *mddev) * may proceed without blocking. It is important to call this before * attempting a GFP_KERNEL allocation while holding the mddev lock. * Must be called with mddev_lock held. + * + * In the ->external case MD_CHANGE_CLEAN can not be cleared until mddev->lock + * is dropped, so return -EAGAIN after notifying userspace. */ -void md_allow_write(mddev_t *mddev) +int md_allow_write(mddev_t *mddev) { if (!mddev->pers) - return; + return 0; if (mddev->ro) - return; + return 0; if (!mddev->pers->sync_request) - return; + return 0; spin_lock_irq(&mddev->write_lock); if (mddev->in_sync) { @@ -5686,14 +5691,14 @@ void md_allow_write(mddev_t *mddev) mddev->safemode = 1; spin_unlock_irq(&mddev->write_lock); md_update_sb(mddev, 0); - sysfs_notify(&mddev->kobj, NULL, "array_state"); - /* wait for the dirty state to be recorded in the metadata */ - wait_event(mddev->sb_wait, - !test_bit(MD_CHANGE_CLEAN, &mddev->flags) && - !test_bit(MD_CHANGE_PENDING, &mddev->flags)); } else spin_unlock_irq(&mddev->write_lock); + + if (test_bit(MD_CHANGE_CLEAN, &mddev->flags)) + return -EAGAIN; + else + return 0; } EXPORT_SYMBOL_GPL(md_allow_write); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index f05d5983efb6..491dc2d4ad5f 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2136,7 +2136,7 @@ static int raid1_reshape(mddev_t *mddev) conf_t *conf = mddev_to_conf(mddev); int cnt, raid_disks; unsigned long flags; - int d, d2; + int d, d2, err; /* Cannot change chunk_size, layout, or level */ if (mddev->chunk_size != mddev->new_chunk || @@ -2148,7 +2148,9 @@ static int raid1_reshape(mddev_t *mddev) return -EINVAL; } - md_allow_write(mddev); + err = md_allow_write(mddev); + if (err) + return err; raid_disks = mddev->raid_disks + mddev->delta_disks; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 442622067cae..8f4c70a53210 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -911,14 +911,16 @@ static int resize_stripes(raid5_conf_t *conf, int newsize) struct stripe_head *osh, *nsh; LIST_HEAD(newstripes); struct disk_info *ndisks; - int err = 0; + int err; struct kmem_cache *sc; int i; if (newsize <= conf->pool_size) return 0; /* never bother to shrink */ - md_allow_write(conf->mddev); + err = md_allow_write(conf->mddev); + if (err) + return err; /* Step 1 */ sc = kmem_cache_create(conf->cache_name[1-conf->active_name], @@ -3843,6 +3845,8 @@ raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len) { raid5_conf_t *conf = mddev_to_conf(mddev); unsigned long new; + int err; + if (len >= PAGE_SIZE) return -EINVAL; if (!conf) @@ -3858,7 +3862,9 @@ raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len) else break; } - md_allow_write(mddev); + err = md_allow_write(mddev); + if (err) + return err; while (new > conf->max_nr_stripes) { if (grow_one_stripe(conf)) conf->max_nr_stripes++; diff --git a/include/linux/raid/md.h b/include/linux/raid/md.h index b7386ae9d288..dc0e3fcb9f28 100644 --- a/include/linux/raid/md.h +++ b/include/linux/raid/md.h @@ -95,7 +95,7 @@ extern int sync_page_io(struct block_device *bdev, sector_t sector, int size, struct page *page, int rw); extern void md_do_sync(mddev_t *mddev); extern void md_new_event(mddev_t *mddev); -extern void md_allow_write(mddev_t *mddev); +extern int md_allow_write(mddev_t *mddev); extern void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev); #endif /* CONFIG_MD */ -- cgit From 9982fbface82893e77d211fbabfbd229da6bdde6 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sun, 6 Jul 2008 14:24:08 +0200 Subject: Revert "cpumask: introduce new APIs" This reverts commit acb7669c125676e63cf96582455509216c39745e. the wrappers are not needed anymore. Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 47418b1b4103..80226e776143 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -394,10 +394,6 @@ int __next_cpu_nr(int n, const cpumask_t *srcp); #endif /* NR_CPUS > 64 */ -#define next_cpu_nr(n, src) next_cpu(n, src) -#define cpus_weight_nr(cpumask) cpus_weight(cpumask) -#define for_each_cpu_mask_nr(cpu, mask) for_each_cpu_mask(cpu, mask) - /* * The following particular system cpumasks and operations manage * possible, present and online cpus. Each of them is a fixed size -- cgit From d6315949ac5527efd00d48283a9e33361c86e8e9 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 22 Jun 2008 12:01:58 +0100 Subject: [ARM] 5096/2: Support Toshiba TC6393XB Mobile I/O Controller. Add support for Toshiba TC6393XB companion chip. Currently only GPIO and part of IRQ features of the device are supported. Signed-off-by: Dmitry Baryshkov Signed-off-by: Russell King --- drivers/mfd/Kconfig | 6 + drivers/mfd/Makefile | 2 + drivers/mfd/tc6393xb.c | 537 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tc6393xb.h | 47 ++++ 4 files changed, 592 insertions(+) create mode 100644 drivers/mfd/tc6393xb.c create mode 100644 include/linux/mfd/tc6393xb.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2566479937c9..1a1ac262fc87 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -38,6 +38,12 @@ config HTC_PASIC3 HTC Magician devices, respectively. Actual functionality is handled by the leds-pasic3 and ds1wm drivers. +config MFD_TC6393XB + bool "Support Toshiba TC6393XB" + depends on HAVE_GPIO_LIB + help + Support for Toshiba Mobile IO Controller TC6393XB + endmenu menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index eef4e26807df..b4168442d53c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -8,6 +8,8 @@ obj-$(CONFIG_MFD_ASIC3) += asic3.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o +obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o + obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c new file mode 100644 index 000000000000..4d7192edefe1 --- /dev/null +++ b/drivers/mfd/tc6393xb.c @@ -0,0 +1,537 @@ +/* + * Toshiba TC6393XB SoC support + * + * Copyright(c) 2005-2006 Chris Humbert + * Copyright(c) 2005 Dirk Opfer + * Copyright(c) 2005 Ian Molton + * Copyright(c) 2007 Dmitry Baryshkov + * + * Based on code written by Sharp/Lineo for 2.4 kernels + * Based on locomo.c + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCR_REVID 0x08 /* b Revision ID */ +#define SCR_ISR 0x50 /* b Interrupt Status */ +#define SCR_IMR 0x52 /* b Interrupt Mask */ +#define SCR_IRR 0x54 /* b Interrupt Routing */ +#define SCR_GPER 0x60 /* w GP Enable */ +#define SCR_GPI_SR(i) (0x64 + (i)) /* b3 GPI Status */ +#define SCR_GPI_IMR(i) (0x68 + (i)) /* b3 GPI INT Mask */ +#define SCR_GPI_EDER(i) (0x6c + (i)) /* b3 GPI Edge Detect Enable */ +#define SCR_GPI_LIR(i) (0x70 + (i)) /* b3 GPI Level Invert */ +#define SCR_GPO_DSR(i) (0x78 + (i)) /* b3 GPO Data Set */ +#define SCR_GPO_DOECR(i) (0x7c + (i)) /* b3 GPO Data OE Control */ +#define SCR_GP_IARCR(i) (0x80 + (i)) /* b3 GP Internal Active Register Control */ +#define SCR_GP_IARLCR(i) (0x84 + (i)) /* b3 GP INTERNAL Active Register Level Control */ +#define SCR_GPI_BCR(i) (0x88 + (i)) /* b3 GPI Buffer Control */ +#define SCR_GPA_IARCR 0x8c /* w GPa Internal Active Register Control */ +#define SCR_GPA_IARLCR 0x90 /* w GPa Internal Active Register Level Control */ +#define SCR_GPA_BCR 0x94 /* w GPa Buffer Control */ +#define SCR_CCR 0x98 /* w Clock Control */ +#define SCR_PLL2CR 0x9a /* w PLL2 Control */ +#define SCR_PLL1CR 0x9c /* l PLL1 Control */ +#define SCR_DIARCR 0xa0 /* b Device Internal Active Register Control */ +#define SCR_DBOCR 0xa1 /* b Device Buffer Off Control */ +#define SCR_FER 0xe0 /* b Function Enable */ +#define SCR_MCR 0xe4 /* w Mode Control */ +#define SCR_CONFIG 0xfc /* b Configuration Control */ +#define SCR_DEBUG 0xff /* b Debug */ + +#define SCR_CCR_CK32K BIT(0) +#define SCR_CCR_USBCK BIT(1) +#define SCR_CCR_UNK1 BIT(4) +#define SCR_CCR_MCLK_MASK (7 << 8) +#define SCR_CCR_MCLK_OFF (0 << 8) +#define SCR_CCR_MCLK_12 (1 << 8) +#define SCR_CCR_MCLK_24 (2 << 8) +#define SCR_CCR_MCLK_48 (3 << 8) +#define SCR_CCR_HCLK_MASK (3 << 12) +#define SCR_CCR_HCLK_24 (0 << 12) +#define SCR_CCR_HCLK_48 (1 << 12) + +#define SCR_FER_USBEN BIT(0) /* USB host enable */ +#define SCR_FER_LCDCVEN BIT(1) /* polysilicon TFT enable */ +#define SCR_FER_SLCDEN BIT(2) /* SLCD enable */ + +#define SCR_MCR_RDY_MASK (3 << 0) +#define SCR_MCR_RDY_OPENDRAIN (0 << 0) +#define SCR_MCR_RDY_TRISTATE (1 << 0) +#define SCR_MCR_RDY_PUSHPULL (2 << 0) +#define SCR_MCR_RDY_UNK BIT(2) +#define SCR_MCR_RDY_EN BIT(3) +#define SCR_MCR_INT_MASK (3 << 4) +#define SCR_MCR_INT_OPENDRAIN (0 << 4) +#define SCR_MCR_INT_TRISTATE (1 << 4) +#define SCR_MCR_INT_PUSHPULL (2 << 4) +#define SCR_MCR_INT_UNK BIT(6) +#define SCR_MCR_INT_EN BIT(7) +/* bits 8 - 16 are unknown */ + +#define TC_GPIO_BIT(i) (1 << (i & 0x7)) + +/*--------------------------------------------------------------------------*/ + +struct tc6393xb { + void __iomem *scr; + + struct gpio_chip gpio; + + struct clk *clk; /* 3,6 Mhz */ + + spinlock_t lock; /* protects RMW cycles */ + + struct { + u8 fer; + u16 ccr; + u8 gpi_bcr[3]; + u8 gpo_dsr[3]; + u8 gpo_doecr[3]; + } suspend_state; + + struct resource rscr; + struct resource *iomem; + int irq; + int irq_base; +}; + +/*--------------------------------------------------------------------------*/ + +static int tc6393xb_gpio_get(struct gpio_chip *chip, + unsigned offset) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + + /* XXX: does dsr also represent inputs? */ + return ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)) + & TC_GPIO_BIT(offset); +} + +static void __tc6393xb_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + u8 dsr; + + dsr = ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8)); + if (value) + dsr |= TC_GPIO_BIT(offset); + else + dsr &= ~TC_GPIO_BIT(offset); + + iowrite8(dsr, tc6393xb->scr + SCR_GPO_DSR(offset / 8)); +} + +static void tc6393xb_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + __tc6393xb_gpio_set(chip, offset, value); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); +} + +static int tc6393xb_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + unsigned long flags; + u8 doecr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + doecr = ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + doecr &= ~TC_GPIO_BIT(offset); + iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +} + +static int tc6393xb_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio); + unsigned long flags; + u8 doecr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + __tc6393xb_gpio_set(chip, offset, value); + + doecr = ioread8(tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + doecr |= TC_GPIO_BIT(offset); + iowrite8(doecr, tc6393xb->scr + SCR_GPO_DOECR(offset / 8)); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +} + +static int tc6393xb_register_gpio(struct tc6393xb *tc6393xb, int gpio_base) +{ + tc6393xb->gpio.label = "tc6393xb"; + tc6393xb->gpio.base = gpio_base; + tc6393xb->gpio.ngpio = 16; + tc6393xb->gpio.set = tc6393xb_gpio_set; + tc6393xb->gpio.get = tc6393xb_gpio_get; + tc6393xb->gpio.direction_input = tc6393xb_gpio_direction_input; + tc6393xb->gpio.direction_output = tc6393xb_gpio_direction_output; + + return gpiochip_add(&tc6393xb->gpio); +} + +/*--------------------------------------------------------------------------*/ + +static void +tc6393xb_irq(unsigned int irq, struct irq_desc *desc) +{ + struct tc6393xb *tc6393xb = get_irq_data(irq); + unsigned int isr; + unsigned int i, irq_base; + + irq_base = tc6393xb->irq_base; + + while ((isr = ioread8(tc6393xb->scr + SCR_ISR) & + ~ioread8(tc6393xb->scr + SCR_IMR))) + for (i = 0; i < TC6393XB_NR_IRQS; i++) { + if (isr & (1 << i)) + generic_handle_irq(irq_base + i); + } +} + +static void tc6393xb_irq_ack(unsigned int irq) +{ +} + +static void tc6393xb_irq_mask(unsigned int irq) +{ + struct tc6393xb *tc6393xb = get_irq_chip_data(irq); + unsigned long flags; + u8 imr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + imr = ioread8(tc6393xb->scr + SCR_IMR); + imr |= 1 << (irq - tc6393xb->irq_base); + iowrite8(imr, tc6393xb->scr + SCR_IMR); + spin_unlock_irqrestore(&tc6393xb->lock, flags); +} + +static void tc6393xb_irq_unmask(unsigned int irq) +{ + struct tc6393xb *tc6393xb = get_irq_chip_data(irq); + unsigned long flags; + u8 imr; + + spin_lock_irqsave(&tc6393xb->lock, flags); + imr = ioread8(tc6393xb->scr + SCR_IMR); + imr &= ~(1 << (irq - tc6393xb->irq_base)); + iowrite8(imr, tc6393xb->scr + SCR_IMR); + spin_unlock_irqrestore(&tc6393xb->lock, flags); +} + +static struct irq_chip tc6393xb_chip = { + .name = "tc6393xb", + .ack = tc6393xb_irq_ack, + .mask = tc6393xb_irq_mask, + .unmask = tc6393xb_irq_unmask, +}; + +static void tc6393xb_attach_irq(struct platform_device *dev) +{ + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + unsigned int irq, irq_base; + + irq_base = tc6393xb->irq_base; + + for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) { + set_irq_chip(irq, &tc6393xb_chip); + set_irq_chip_data(irq, tc6393xb); + set_irq_handler(irq, handle_edge_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + set_irq_type(tc6393xb->irq, IRQT_FALLING); + set_irq_data(tc6393xb->irq, tc6393xb); + set_irq_chained_handler(tc6393xb->irq, tc6393xb_irq); +} + +static void tc6393xb_detach_irq(struct platform_device *dev) +{ + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + unsigned int irq, irq_base; + + set_irq_chained_handler(tc6393xb->irq, NULL); + set_irq_data(tc6393xb->irq, NULL); + + irq_base = tc6393xb->irq_base; + + for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) { + set_irq_flags(irq, 0); + set_irq_chip(irq, NULL); + set_irq_chip_data(irq, NULL); + } +} + +/*--------------------------------------------------------------------------*/ + +static int tc6393xb_hw_init(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + int i; + + iowrite8(tc6393xb->suspend_state.fer, tc6393xb->scr + SCR_FER); + iowrite16(tcpd->scr_pll2cr, tc6393xb->scr + SCR_PLL2CR); + iowrite16(tc6393xb->suspend_state.ccr, tc6393xb->scr + SCR_CCR); + iowrite16(SCR_MCR_RDY_OPENDRAIN | SCR_MCR_RDY_UNK | SCR_MCR_RDY_EN | + SCR_MCR_INT_OPENDRAIN | SCR_MCR_INT_UNK | SCR_MCR_INT_EN | + BIT(15), tc6393xb->scr + SCR_MCR); + iowrite16(tcpd->scr_gper, tc6393xb->scr + SCR_GPER); + iowrite8(0, tc6393xb->scr + SCR_IRR); + iowrite8(0xbf, tc6393xb->scr + SCR_IMR); + + for (i = 0; i < 3; i++) { + iowrite8(tc6393xb->suspend_state.gpo_dsr[i], + tc6393xb->scr + SCR_GPO_DSR(i)); + iowrite8(tc6393xb->suspend_state.gpo_doecr[i], + tc6393xb->scr + SCR_GPO_DOECR(i)); + iowrite8(tc6393xb->suspend_state.gpi_bcr[i], + tc6393xb->scr + SCR_GPI_BCR(i)); + } + + return 0; +} + +static int __devinit tc6393xb_probe(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb; + struct resource *iomem; + struct resource *rscr; + int retval, temp; + int i; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) + return -EINVAL; + + tc6393xb = kzalloc(sizeof *tc6393xb, GFP_KERNEL); + if (!tc6393xb) { + retval = -ENOMEM; + goto err_kzalloc; + } + + spin_lock_init(&tc6393xb->lock); + + platform_set_drvdata(dev, tc6393xb); + tc6393xb->iomem = iomem; + tc6393xb->irq = platform_get_irq(dev, 0); + tc6393xb->irq_base = tcpd->irq_base; + + tc6393xb->clk = clk_get(&dev->dev, "GPIO27_CLK" /* "CK3P6MI" */); + if (IS_ERR(tc6393xb->clk)) { + retval = PTR_ERR(tc6393xb->clk); + goto err_clk_get; + } + + rscr = &tc6393xb->rscr; + rscr->name = "tc6393xb-core"; + rscr->start = iomem->start; + rscr->end = iomem->start + 0xff; + rscr->flags = IORESOURCE_MEM; + + retval = request_resource(iomem, rscr); + if (retval) + goto err_request_scr; + + tc6393xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1); + if (!tc6393xb->scr) { + retval = -ENOMEM; + goto err_ioremap; + } + + retval = clk_enable(tc6393xb->clk); + if (retval) + goto err_clk_enable; + + retval = tcpd->enable(dev); + if (retval) + goto err_enable; + + tc6393xb->suspend_state.fer = 0; + for (i = 0; i < 3; i++) { + tc6393xb->suspend_state.gpo_dsr[i] = + (tcpd->scr_gpo_dsr >> (8 * i)) & 0xff; + tc6393xb->suspend_state.gpo_doecr[i] = + (tcpd->scr_gpo_doecr >> (8 * i)) & 0xff; + } + /* + * It may be necessary to change this back to + * platform-dependant code + */ + tc6393xb->suspend_state.ccr = SCR_CCR_UNK1 | + SCR_CCR_HCLK_48; + + retval = tc6393xb_hw_init(dev); + if (retval) + goto err_hw_init; + + printk(KERN_INFO "Toshiba tc6393xb revision %d at 0x%08lx, irq %d\n", + ioread8(tc6393xb->scr + SCR_REVID), + (unsigned long) iomem->start, tc6393xb->irq); + + tc6393xb->gpio.base = -1; + + if (tcpd->gpio_base >= 0) { + retval = tc6393xb_register_gpio(tc6393xb, tcpd->gpio_base); + if (retval) + goto err_gpio_add; + } + + if (tc6393xb->irq) + tc6393xb_attach_irq(dev); + + return 0; + + if (tc6393xb->irq) + tc6393xb_detach_irq(dev); + +err_gpio_add: + if (tc6393xb->gpio.base != -1) + temp = gpiochip_remove(&tc6393xb->gpio); +err_hw_init: + tcpd->disable(dev); +err_clk_enable: + clk_disable(tc6393xb->clk); +err_enable: + iounmap(tc6393xb->scr); +err_ioremap: + release_resource(&tc6393xb->rscr); +err_request_scr: + clk_put(tc6393xb->clk); +err_clk_get: + kfree(tc6393xb); +err_kzalloc: + return retval; +} + +static int __devexit tc6393xb_remove(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + int ret; + + if (tc6393xb->irq) + tc6393xb_detach_irq(dev); + + if (tc6393xb->gpio.base != -1) { + ret = gpiochip_remove(&tc6393xb->gpio); + if (ret) { + dev_err(&dev->dev, "Can't remove gpio chip: %d\n", ret); + return ret; + } + } + + ret = tcpd->disable(dev); + + clk_disable(tc6393xb->clk); + + iounmap(tc6393xb->scr); + + release_resource(&tc6393xb->rscr); + + platform_set_drvdata(dev, NULL); + + clk_put(tc6393xb->clk); + + kfree(tc6393xb); + + return ret; +} + +#ifdef CONFIG_PM +static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + int i; + + + tc6393xb->suspend_state.ccr = ioread16(tc6393xb->scr + SCR_CCR); + tc6393xb->suspend_state.fer = ioread8(tc6393xb->scr + SCR_FER); + + for (i = 0; i < 3; i++) { + tc6393xb->suspend_state.gpo_dsr[i] = + ioread8(tc6393xb->scr + SCR_GPO_DSR(i)); + tc6393xb->suspend_state.gpo_doecr[i] = + ioread8(tc6393xb->scr + SCR_GPO_DOECR(i)); + tc6393xb->suspend_state.gpi_bcr[i] = + ioread8(tc6393xb->scr + SCR_GPI_BCR(i)); + } + + return tcpd->suspend(dev); +} + +static int tc6393xb_resume(struct platform_device *dev) +{ + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + int ret = tcpd->resume(dev); + + if (ret) + return ret; + + return tc6393xb_hw_init(dev); +} +#else +#define tc6393xb_suspend NULL +#define tc6393xb_resume NULL +#endif + +static struct platform_driver tc6393xb_driver = { + .probe = tc6393xb_probe, + .remove = __devexit_p(tc6393xb_remove), + .suspend = tc6393xb_suspend, + .resume = tc6393xb_resume, + + .driver = { + .name = "tc6393xb", + .owner = THIS_MODULE, + }, +}; + +static int __init tc6393xb_init(void) +{ + return platform_driver_register(&tc6393xb_driver); +} + +static void __exit tc6393xb_exit(void) +{ + platform_driver_unregister(&tc6393xb_driver); +} + +subsys_initcall(tc6393xb_init); +module_exit(tc6393xb_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov and Dirk Opfer"); +MODULE_DESCRIPTION("tc6393xb Toshiba Mobile IO Controller"); +MODULE_ALIAS("platform:tc6393xb"); diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h new file mode 100644 index 000000000000..0e3dd4ca523b --- /dev/null +++ b/include/linux/mfd/tc6393xb.h @@ -0,0 +1,47 @@ +/* + * Toshiba TC6393XB SoC support + * + * Copyright(c) 2005-2006 Chris Humbert + * Copyright(c) 2005 Dirk Opfer + * Copyright(c) 2005 Ian Molton + * Copyright(c) 2007 Dmitry Baryshkov + * + * Based on code written by Sharp/Lineo for 2.4 kernels + * Based on locomo.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef TC6393XB_H +#define TC6393XB_H + +/* Also one should provide the CK3P6MI clock */ +struct tc6393xb_platform_data { + u16 scr_pll2cr; /* PLL2 Control */ + u16 scr_gper; /* GP Enable */ + u32 scr_gpo_doecr; /* GPO Data OE Control */ + u32 scr_gpo_dsr; /* GPO Data Set */ + + int (*enable)(struct platform_device *dev); + int (*disable)(struct platform_device *dev); + int (*suspend)(struct platform_device *dev); + int (*resume)(struct platform_device *dev); + + int irq_base; /* a base for cascaded irq */ + int gpio_base; +}; + +/* + * Relative to irq_base + */ +#define IRQ_TC6393_NAND 0 +#define IRQ_TC6393_MMC 1 +#define IRQ_TC6393_OHCI 2 +#define IRQ_TC6393_SERIAL 3 +#define IRQ_TC6393_FB 4 + +#define TC6393XB_NR_IRQS 8 + +#endif -- cgit From aa613de676986f136fa6f48a4d709b5d264f4f38 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Jun 2008 10:37:19 +0100 Subject: [ARM] 5127/1: Core MFD support This patch provides a common subdevice registration system for MFD type chips, using platfrom device. Signed-off-by: Ian Molton Signed-off-by: Dmitry Baryshkov Signed-off-by: Russell King --- drivers/mfd/Kconfig | 4 ++ drivers/mfd/Makefile | 2 + drivers/mfd/mfd-core.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/core.h | 55 +++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 drivers/mfd/mfd-core.c create mode 100644 include/linux/mfd/core.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1a1ac262fc87..8ebc0be10953 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -5,6 +5,10 @@ menu "Multifunction device drivers" depends on HAS_IOMEM +config MFD_CORE + tristate + default n + config MFD_SM501 tristate "Support for Silicon Motion SM501" ---help--- diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index b4168442d53c..33daa2f45dd8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o +obj-$(CONFIG_MFD_CORE) += mfd-core.o + obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c new file mode 100644 index 000000000000..d7d88ce053a6 --- /dev/null +++ b/drivers/mfd/mfd-core.c @@ -0,0 +1,114 @@ +/* + * drivers/mfd/mfd-core.c + * + * core MFD support + * Copyright (c) 2006 Ian Molton + * Copyright (c) 2007,2008 Dmitry Baryshkov + * + * 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 +#include +#include + +static int mfd_add_device(struct platform_device *parent, + const struct mfd_cell *cell, + struct resource *mem_base, + int irq_base) +{ + struct resource res[cell->num_resources]; + struct platform_device *pdev; + int ret = -ENOMEM; + int r; + + pdev = platform_device_alloc(cell->name, parent->id); + if (!pdev) + goto fail_alloc; + + pdev->dev.parent = &parent->dev; + + ret = platform_device_add_data(pdev, + cell, sizeof(struct mfd_cell)); + if (ret) + goto fail_device; + + memzero(res, sizeof(res)); + for (r = 0; r < cell->num_resources; r++) { + res[r].name = cell->resources[r].name; + res[r].flags = cell->resources[r].flags; + + /* Find out base to use */ + if (cell->resources[r].flags & IORESOURCE_MEM) { + res[r].parent = mem_base; + res[r].start = mem_base->start + + cell->resources[r].start; + res[r].end = mem_base->start + + cell->resources[r].end; + } else if (cell->resources[r].flags & IORESOURCE_IRQ) { + res[r].start = irq_base + + cell->resources[r].start; + res[r].end = irq_base + + cell->resources[r].end; + } else { + res[r].parent = cell->resources[r].parent; + res[r].start = cell->resources[r].start; + res[r].end = cell->resources[r].end; + } + } + + platform_device_add_resources(pdev, res, cell->num_resources); + + ret = platform_device_add(pdev); + if (ret) + goto fail_device; + + return 0; + +/* platform_device_del(pdev); */ +fail_device: + platform_device_put(pdev); +fail_alloc: + return ret; +} + +int mfd_add_devices( + struct platform_device *parent, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base) +{ + int i; + int ret = 0; + + for (i = 0; i < n_devs; i++) { + ret = mfd_add_device(parent, cells + i, mem_base, irq_base); + if (ret) + break; + } + + if (ret) + mfd_remove_devices(parent); + + return ret; +} +EXPORT_SYMBOL(mfd_add_devices); + +static int mfd_remove_devices_fn(struct device *dev, void *unused) +{ + platform_device_unregister( + container_of(dev, struct platform_device, dev)); + return 0; +} + +void mfd_remove_devices(struct platform_device *parent) +{ + device_for_each_child(&parent->dev, NULL, mfd_remove_devices_fn); +} +EXPORT_SYMBOL(mfd_remove_devices); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov"); diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h new file mode 100644 index 000000000000..bb3dd0545928 --- /dev/null +++ b/include/linux/mfd/core.h @@ -0,0 +1,55 @@ +#ifndef MFD_CORE_H +#define MFD_CORE_H +/* + * drivers/mfd/mfd-core.h + * + * core MFD support + * Copyright (c) 2006 Ian Molton + * Copyright (c) 2007 Dmitry Baryshkov + * + * 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 + +/* + * This struct describes the MFD part ("cell"). + * After registration the copy of this structure will become the platform data + * of the resulting platform_device + */ +struct mfd_cell { + const char *name; + + int (*enable)(struct platform_device *dev); + int (*disable)(struct platform_device *dev); + int (*suspend)(struct platform_device *dev); + int (*resume)(struct platform_device *dev); + + void *driver_data; /* driver-specific data */ + + /* + * This resources can be specified relatievly to the parent device. + * For accessing device you should use resources from device + */ + int num_resources; + const struct resource *resources; +}; + +static inline struct mfd_cell * +mfd_get_cell(struct platform_device *pdev) +{ + return (struct mfd_cell *)pdev->dev.platform_data; +} + +extern int mfd_add_devices( + struct platform_device *parent, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base); + +extern void mfd_remove_devices(struct platform_device *parent); + +#endif -- cgit From f024ff10b1ab13da4d626366019fd05c49721af7 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 27 Jun 2008 10:37:57 +0100 Subject: [ARM] 5128/1: tc6393xb: tmio-nand support Signed-off-by: Dmitry Baryshkov Signed-off-by: Russell King --- drivers/mfd/Kconfig | 1 + drivers/mfd/tc6393xb.c | 63 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tc6393xb.h | 2 ++ include/linux/mfd/tmio.h | 17 ++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 include/linux/mfd/tmio.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8ebc0be10953..7dff105e8f83 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -45,6 +45,7 @@ config HTC_PASIC3 config MFD_TC6393XB bool "Support Toshiba TC6393XB" depends on HAVE_GPIO_LIB + select MFD_CORE help Support for Toshiba Mobile IO Controller TC6393XB diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 4d7192edefe1..2d87501b6fd4 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -108,6 +110,59 @@ struct tc6393xb { int irq_base; }; +enum { + TC6393XB_CELL_NAND, +}; + +/*--------------------------------------------------------------------------*/ + +static int tc6393xb_nand_enable(struct platform_device *nand) +{ + struct platform_device *dev = to_platform_device(nand->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + + /* SMD buffer on */ + dev_dbg(&dev->dev, "SMD buffer on\n"); + iowrite8(0xff, tc6393xb->scr + SCR_GPI_BCR(1)); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +} + +static struct resource __devinitdata tc6393xb_nand_resources[] = { + { + .name = TMIO_NAND_CONFIG, + .start = 0x0100, + .end = 0x01ff, + .flags = IORESOURCE_MEM, + }, + { + .name = TMIO_NAND_CONTROL, + .start = 0x1000, + .end = 0x1007, + .flags = IORESOURCE_MEM, + }, + { + .name = TMIO_NAND_IRQ, + .start = IRQ_TC6393_NAND, + .end = IRQ_TC6393_NAND, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell __devinitdata tc6393xb_cells[] = { + [TC6393XB_CELL_NAND] = { + .name = "tmio-nand", + .enable = tc6393xb_nand_enable, + .num_resources = ARRAY_SIZE(tc6393xb_nand_resources), + .resources = tc6393xb_nand_resources, + }, +}; + /*--------------------------------------------------------------------------*/ static int tc6393xb_gpio_get(struct gpio_chip *chip, @@ -410,6 +465,12 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) if (tc6393xb->irq) tc6393xb_attach_irq(dev); + tc6393xb_cells[TC6393XB_CELL_NAND].driver_data = tcpd->nand_data; + + retval = mfd_add_devices(dev, + tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), + iomem, tcpd->irq_base); + return 0; if (tc6393xb->irq) @@ -440,6 +501,8 @@ static int __devexit tc6393xb_remove(struct platform_device *dev) struct tc6393xb *tc6393xb = platform_get_drvdata(dev); int ret; + mfd_remove_devices(dev); + if (tc6393xb->irq) tc6393xb_detach_irq(dev); diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h index 0e3dd4ca523b..7cc824a58f7c 100644 --- a/include/linux/mfd/tc6393xb.h +++ b/include/linux/mfd/tc6393xb.h @@ -31,6 +31,8 @@ struct tc6393xb_platform_data { int irq_base; /* a base for cascaded irq */ int gpio_base; + + struct tmio_nand_data *nand_data; }; /* diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h new file mode 100644 index 000000000000..9438d8c9ac1c --- /dev/null +++ b/include/linux/mfd/tmio.h @@ -0,0 +1,17 @@ +#ifndef MFD_TMIO_H +#define MFD_TMIO_H + +/* + * data for the NAND controller + */ +struct tmio_nand_data { + struct nand_bbt_descr *badblock_pattern; + struct mtd_partition *partition; + unsigned int num_partitions; +}; + +#define TMIO_NAND_CONFIG "tmio-nand-config" +#define TMIO_NAND_CONTROL "tmio-nand-control" +#define TMIO_NAND_IRQ "tmio-nand" + +#endif -- cgit From 7cc5bf9a3a84e5a02e23e5739fb894790b37c101 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 8 Jul 2008 11:58:21 -0700 Subject: dmaengine: track the number of clients using a channel Haavard's dma-slave interface would like to test for exclusive access to a channel. The standard channel refcounting is not sufficient in that it tracks more than just client references, it is also inaccurate as reference counts are percpu until the channel is removed. This change also enables a future fix to deallocate resources when a client declines to use a capable channel. Acked-by: Haavard Skinnemoen Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 14 ++++++++++---- include/linux/dmaengine.h | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 99c22b42bada..10de69eb1a3e 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -183,9 +183,10 @@ static void dma_client_chan_alloc(struct dma_client *client) /* we are done once this client rejects * an available resource */ - if (ack == DMA_ACK) + if (ack == DMA_ACK) { dma_chan_get(chan); - else if (ack == DMA_NAK) + chan->client_count++; + } else if (ack == DMA_NAK) return; } } @@ -272,8 +273,10 @@ static void dma_clients_notify_removed(struct dma_chan *chan) /* client was holding resources for this channel so * free it */ - if (ack == DMA_ACK) + if (ack == DMA_ACK) { dma_chan_put(chan); + chan->client_count--; + } } mutex_unlock(&dma_list_mutex); @@ -313,8 +316,10 @@ void dma_async_client_unregister(struct dma_client *client) ack = client->event_callback(client, chan, DMA_RESOURCE_REMOVED); - if (ack == DMA_ACK) + if (ack == DMA_ACK) { dma_chan_put(chan); + chan->client_count--; + } } list_del(&client->global_node); @@ -394,6 +399,7 @@ int dma_async_device_register(struct dma_device *device) kref_get(&device->refcount); kref_get(&device->refcount); kref_init(&chan->refcount); + chan->client_count = 0; chan->slow_ref = 0; INIT_RCU_HEAD(&chan->rcu); } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index d08a5c5eb928..6432b8343220 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -139,6 +139,7 @@ struct dma_chan_percpu { * @rcu: the DMA channel's RCU head * @device_node: used to add this to the device chan list * @local: per-cpu pointer to a struct dma_chan_percpu + * @client-count: how many clients are using this channel */ struct dma_chan { struct dma_device *device; @@ -154,6 +155,7 @@ struct dma_chan { struct list_head device_node; struct dma_chan_percpu *local; + int client_count; }; #define to_dma_chan(p) container_of(p, struct dma_chan, dev) -- cgit From 848c536a37b8db4e461f14ca15fe29850151c822 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 8 Jul 2008 11:58:58 -0700 Subject: dmaengine: Add dma_client parameter to device_alloc_chan_resources A DMA controller capable of doing slave transfers may need to know a few things about the slave when preparing the channel. We don't want to add this information to struct dma_channel since the channel hasn't yet been bound to a client at this point. Instead, pass a reference to the client requesting the channel to the driver's device_alloc_chan_resources hook so that it can pick the necessary information from the dma_client struct by itself. [dan.j.williams@intel.com: fixed up fsldma and mv_xor] Acked-by: Maciej Sosnowski Signed-off-by: Haavard Skinnemoen Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 3 ++- drivers/dma/fsldma.c | 7 ++++--- drivers/dma/ioat_dma.c | 5 +++-- drivers/dma/iop-adma.c | 7 ++++--- drivers/dma/mv_xor.c | 7 ++++--- include/linux/dmaengine.h | 3 ++- 6 files changed, 19 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 10de69eb1a3e..7344f5dbd501 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -174,7 +174,8 @@ static void dma_client_chan_alloc(struct dma_client *client) if (!dma_chan_satisfies_mask(chan, client->cap_mask)) continue; - desc = chan->device->device_alloc_chan_resources(chan); + desc = chan->device->device_alloc_chan_resources( + chan, client); if (desc >= 0) { ack = client->event_callback(client, chan, diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 724f6fdd0af6..c0059ca58340 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -366,7 +366,8 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor( * * Return - The number of descriptors allocated. */ -static int fsl_dma_alloc_chan_resources(struct dma_chan *chan) +static int fsl_dma_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan); LIST_HEAD(tmp_list); @@ -819,7 +820,7 @@ static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan) chan = &fsl_chan->common; - if (fsl_dma_alloc_chan_resources(chan) < 1) { + if (fsl_dma_alloc_chan_resources(chan, NULL) < 1) { dev_err(fsl_chan->dev, "selftest: Cannot alloc resources for DMA\n"); err = -ENODEV; @@ -847,7 +848,7 @@ static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan) /* Test free and re-alloc channel resources */ fsl_dma_free_chan_resources(chan); - if (fsl_dma_alloc_chan_resources(chan) < 1) { + if (fsl_dma_alloc_chan_resources(chan, NULL) < 1) { dev_err(fsl_chan->dev, "selftest: Cannot alloc resources for DMA\n"); err = -ENODEV; diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 318e8a22d814..90e5b0a28cbf 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -452,7 +452,8 @@ static void ioat2_dma_massage_chan_desc(struct ioat_dma_chan *ioat_chan) * ioat_dma_alloc_chan_resources - returns the number of allocated descriptors * @chan: the channel to be filled out */ -static int ioat_dma_alloc_chan_resources(struct dma_chan *chan) +static int ioat_dma_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); struct ioat_desc_sw *desc; @@ -1049,7 +1050,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (device->common.device_alloc_chan_resources(dma_chan) < 1) { + if (device->common.device_alloc_chan_resources(dma_chan, NULL) < 1) { dev_err(&device->pdev->dev, "selftest cannot allocate chan resource\n"); err = -ENODEV; diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 4e6b052c0654..b57564dd0232 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -444,7 +444,8 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan); static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan); /* returns the number of allocated descriptors */ -static int iop_adma_alloc_chan_resources(struct dma_chan *chan) +static int iop_adma_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { char *hw_desc; int idx; @@ -838,7 +839,7 @@ static int __devinit iop_adma_memcpy_self_test(struct iop_adma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (iop_adma_alloc_chan_resources(dma_chan) < 1) { + if (iop_adma_alloc_chan_resources(dma_chan, NULL) < 1) { err = -ENODEV; goto out; } @@ -936,7 +937,7 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (iop_adma_alloc_chan_resources(dma_chan) < 1) { + if (iop_adma_alloc_chan_resources(dma_chan, NULL) < 1) { err = -ENODEV; goto out; } diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index f0c123ce8ae0..8239cfdbc2e6 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -588,7 +588,8 @@ submit_done: } /* returns the number of allocated descriptors */ -static int mv_xor_alloc_chan_resources(struct dma_chan *chan) +static int mv_xor_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) { char *hw_desc; int idx; @@ -938,7 +939,7 @@ static int __devinit mv_xor_memcpy_self_test(struct mv_xor_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (mv_xor_alloc_chan_resources(dma_chan) < 1) { + if (mv_xor_alloc_chan_resources(dma_chan, NULL) < 1) { err = -ENODEV; goto out; } @@ -1033,7 +1034,7 @@ mv_xor_xor_self_test(struct mv_xor_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (mv_xor_alloc_chan_resources(dma_chan) < 1) { + if (mv_xor_alloc_chan_resources(dma_chan, NULL) < 1) { err = -ENODEV; goto out; } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 6432b8343220..ba89b0f5056e 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -281,7 +281,8 @@ struct dma_device { int dev_id; struct device *dev; - int (*device_alloc_chan_resources)(struct dma_chan *chan); + int (*device_alloc_chan_resources)(struct dma_chan *chan, + struct dma_client *client); void (*device_free_chan_resources)(struct dma_chan *chan); struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)( -- cgit From e1d181efb14a93cf263d6c588a5395518edf3294 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 4 Jul 2008 00:13:40 -0700 Subject: dmaengine: add DMA_COMPL_SKIP_{SRC,DEST}_UNMAP flags to control dma unmap In some cases client code may need the dma-driver to skip the unmap of source and/or destination buffers. Setting these flags indicates to the driver to skip the unmap step. In this regard async_xor is currently broken in that it allows the destination buffer to be unmapped while an operation is still in progress, i.e. when the number of sources exceeds the hardware channel's maximum (fixed in a subsequent patch). Acked-by: Saeed Bishara Acked-by: Maciej Sosnowski Acked-by: Haavard Skinnemoen Signed-off-by: Dan Williams --- drivers/dma/ioat_dma.c | 48 +++++++++++++++++++++++------------------------ drivers/dma/iop-adma.c | 29 +++++++++++++++++----------- drivers/dma/mv_xor.c | 22 ++++++++++++++++------ include/linux/dmaengine.h | 4 ++++ 4 files changed, 61 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 90e5b0a28cbf..171cad69f318 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -757,6 +757,27 @@ static void ioat_dma_cleanup_tasklet(unsigned long data) chan->reg_base + IOAT_CHANCTRL_OFFSET); } +static void +ioat_dma_unmap(struct ioat_dma_chan *ioat_chan, struct ioat_desc_sw *desc) +{ + /* + * yes we are unmapping both _page and _single + * alloc'd regions with unmap_page. Is this + * *really* that bad? + */ + if (!(desc->async_tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) + pci_unmap_page(ioat_chan->device->pdev, + pci_unmap_addr(desc, dst), + pci_unmap_len(desc, len), + PCI_DMA_FROMDEVICE); + + if (!(desc->async_tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) + pci_unmap_page(ioat_chan->device->pdev, + pci_unmap_addr(desc, src), + pci_unmap_len(desc, len), + PCI_DMA_TODEVICE); +} + /** * ioat_dma_memcpy_cleanup - cleanup up finished descriptors * @chan: ioat channel to be cleaned up @@ -817,21 +838,7 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) */ if (desc->async_tx.cookie) { cookie = desc->async_tx.cookie; - - /* - * yes we are unmapping both _page and _single - * alloc'd regions with unmap_page. Is this - * *really* that bad? - */ - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, dst), - pci_unmap_len(desc, len), - PCI_DMA_FROMDEVICE); - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, src), - pci_unmap_len(desc, len), - PCI_DMA_TODEVICE); - + ioat_dma_unmap(ioat_chan, desc); if (desc->async_tx.callback) { desc->async_tx.callback(desc->async_tx.callback_param); desc->async_tx.callback = NULL; @@ -890,16 +897,7 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) if (desc->async_tx.cookie) { cookie = desc->async_tx.cookie; desc->async_tx.cookie = 0; - - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, dst), - pci_unmap_len(desc, len), - PCI_DMA_FROMDEVICE); - pci_unmap_page(ioat_chan->device->pdev, - pci_unmap_addr(desc, src), - pci_unmap_len(desc, len), - PCI_DMA_TODEVICE); - + ioat_dma_unmap(ioat_chan, desc); if (desc->async_tx.callback) { desc->async_tx.callback(desc->async_tx.callback_param); desc->async_tx.callback = NULL; diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index b57564dd0232..434013d41288 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -82,17 +82,24 @@ iop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc, struct device *dev = &iop_chan->device->pdev->dev; u32 len = unmap->unmap_len; - u32 src_cnt = unmap->unmap_src_cnt; - dma_addr_t addr = iop_desc_get_dest_addr(unmap, - iop_chan); - - dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); - while (src_cnt--) { - addr = iop_desc_get_src_addr(unmap, - iop_chan, - src_cnt); - dma_unmap_page(dev, addr, len, - DMA_TO_DEVICE); + enum dma_ctrl_flags flags = desc->async_tx.flags; + u32 src_cnt; + dma_addr_t addr; + + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + addr = iop_desc_get_dest_addr(unmap, iop_chan); + dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); + } + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + src_cnt = unmap->unmap_src_cnt; + while (src_cnt--) { + addr = iop_desc_get_src_addr(unmap, + iop_chan, + src_cnt); + dma_unmap_page(dev, addr, len, + DMA_TO_DEVICE); + } } desc->group_head = NULL; } diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 8239cfdbc2e6..a4e4494663bf 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -308,13 +308,23 @@ mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc, struct device *dev = &mv_chan->device->pdev->dev; u32 len = unmap->unmap_len; - u32 src_cnt = unmap->unmap_src_cnt; - dma_addr_t addr = mv_desc_get_dest_addr(unmap); + enum dma_ctrl_flags flags = desc->async_tx.flags; + u32 src_cnt; + dma_addr_t addr; - dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); - while (src_cnt--) { - addr = mv_desc_get_src_addr(unmap, src_cnt); - dma_unmap_page(dev, addr, len, DMA_TO_DEVICE); + if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + addr = mv_desc_get_dest_addr(unmap); + dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE); + } + + if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + src_cnt = unmap->unmap_src_cnt; + while (src_cnt--) { + addr = mv_desc_get_src_addr(unmap, + src_cnt); + dma_unmap_page(dev, addr, len, + DMA_TO_DEVICE); + } } desc->group_head = NULL; } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index ba89b0f5056e..b058d6360383 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -102,10 +102,14 @@ enum dma_transaction_type { * @DMA_CTRL_ACK - the descriptor cannot be reused until the client * acknowledges receipt, i.e. has has a chance to establish any * dependency chains + * @DMA_COMPL_SKIP_SRC_UNMAP - set to disable dma-unmapping the source buffer(s) + * @DMA_COMPL_SKIP_DEST_UNMAP - set to disable dma-unmapping the destination(s) */ enum dma_ctrl_flags { DMA_PREP_INTERRUPT = (1 << 0), DMA_CTRL_ACK = (1 << 1), + DMA_COMPL_SKIP_SRC_UNMAP = (1 << 2), + DMA_COMPL_SKIP_DEST_UNMAP = (1 << 3), }; /** -- cgit From dc0ee6435cb92ccc81b14ff28d163fecc5a7f120 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 8 Jul 2008 11:59:35 -0700 Subject: dmaengine: Add slave DMA interface This patch adds the necessary interfaces to the DMA Engine framework to use functionality found on most embedded DMA controllers: DMA from and to I/O registers with hardware handshaking. In this context, hardware hanshaking means that the peripheral that owns the I/O registers in question is able to tell the DMA controller when more data is available for reading, or when there is room for more data to be written. This usually happens internally on the chip, but these signals may also be exported outside the chip for things like IDE DMA, etc. A new struct dma_slave is introduced. This contains information that the DMA engine driver needs to set up slave transfers to and from a slave device. Most engines supporting DMA slave transfers will want to extend this structure with controller-specific parameters. This additional information is usually passed from the platform/board code through the client driver. A "slave" pointer is added to the dma_client struct. This must point to a valid dma_slave structure iff the DMA_SLAVE capability is requested. The DMA engine driver may use this information in its device_alloc_chan_resources hook to configure the DMA controller for slave transfers from and to the given slave device. A new operation for preparing slave DMA transfers is added to struct dma_device. This takes a scatterlist and returns a single descriptor representing the whole transfer. Another new operation for terminating all pending transfers is added as well. The latter is needed because there may be errors outside the scope of the DMA Engine framework that may require DMA operations to be terminated prematurely. DMA Engine drivers may extend the dma_device, dma_chan and/or dma_slave_descriptor structures to allow controller-specific operations. The client driver can detect such extensions by looking at the DMA Engine's struct device, or it can request a specific DMA Engine device by setting the dma_dev field in struct dma_slave. dmaslave interface changes since v4: * Fix checkpatch errors * Fix changelog (there are no slave descriptors anymore) dmaslave interface changes since v3: * Use dma_data_direction instead of a new enum * Submit slave transfers as scatterlists * Remove the DMA slave descriptor struct dmaslave interface changes since v2: * Add a dma_dev field to struct dma_slave. If set, the client can only be bound to the DMA controller that corresponds to this device. This allows controller-specific extensions of the dma_slave structure; if the device matches, the controller may safely assume its extensions are present. * Move reg_width into struct dma_slave as there are currently no users that need to be able to set the width on a per-transfer basis. dmaslave interface changes since v1: * Drop the set_direction and set_width descriptor hooks. Pass the direction and width to the prep function instead. * Declare a dma_slave struct with fixed information about a slave, i.e. register addresses, handshake interfaces and such. * Add pointer to a dma_slave struct to dma_client. Can be NULL if the DMA_SLAVE capability isn't requested. * Drop the set_slave device hook since the alloc_chan_resources hook now has enough information to set up the channel for slave transfers. Acked-by: Maciej Sosnowski Signed-off-by: Haavard Skinnemoen Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 16 ++++++++++++++- include/linux/dmaengine.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 7344f5dbd501..dc003a3a787d 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -169,7 +169,12 @@ static void dma_client_chan_alloc(struct dma_client *client) enum dma_state_client ack; /* Find a channel */ - list_for_each_entry(device, &dma_device_list, global_node) + list_for_each_entry(device, &dma_device_list, global_node) { + /* Does the client require a specific DMA controller? */ + if (client->slave && client->slave->dma_dev + && client->slave->dma_dev != device->dev) + continue; + list_for_each_entry(chan, &device->channels, device_node) { if (!dma_chan_satisfies_mask(chan, client->cap_mask)) continue; @@ -191,6 +196,7 @@ static void dma_client_chan_alloc(struct dma_client *client) return; } } + } } enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) @@ -289,6 +295,10 @@ static void dma_clients_notify_removed(struct dma_chan *chan) */ void dma_async_client_register(struct dma_client *client) { + /* validate client data */ + BUG_ON(dma_has_cap(DMA_SLAVE, client->cap_mask) && + !client->slave); + mutex_lock(&dma_list_mutex); list_add_tail(&client->global_node, &dma_client_list); mutex_unlock(&dma_list_mutex); @@ -365,6 +375,10 @@ int dma_async_device_register(struct dma_device *device) !device->device_prep_dma_memset); BUG_ON(dma_has_cap(DMA_INTERRUPT, device->cap_mask) && !device->device_prep_dma_interrupt); + BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && + !device->device_prep_slave_sg); + BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) && + !device->device_terminate_all); BUG_ON(!device->device_alloc_chan_resources); BUG_ON(!device->device_free_chan_resources); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index b058d6360383..9b91d341e1fa 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -89,10 +89,23 @@ enum dma_transaction_type { DMA_MEMSET, DMA_MEMCPY_CRC32C, DMA_INTERRUPT, + DMA_SLAVE, }; /* last transaction type for creation of the capabilities mask */ -#define DMA_TX_TYPE_END (DMA_INTERRUPT + 1) +#define DMA_TX_TYPE_END (DMA_SLAVE + 1) + +/** + * enum dma_slave_width - DMA slave register access width. + * @DMA_SLAVE_WIDTH_8BIT: Do 8-bit slave register accesses + * @DMA_SLAVE_WIDTH_16BIT: Do 16-bit slave register accesses + * @DMA_SLAVE_WIDTH_32BIT: Do 32-bit slave register accesses + */ +enum dma_slave_width { + DMA_SLAVE_WIDTH_8BIT, + DMA_SLAVE_WIDTH_16BIT, + DMA_SLAVE_WIDTH_32BIT, +}; /** * enum dma_ctrl_flags - DMA flags to augment operation preparation, @@ -118,6 +131,32 @@ enum dma_ctrl_flags { */ typedef struct { DECLARE_BITMAP(bits, DMA_TX_TYPE_END); } dma_cap_mask_t; +/** + * struct dma_slave - Information about a DMA slave + * @dev: device acting as DMA slave + * @dma_dev: required DMA master device. If non-NULL, the client can not be + * bound to other masters than this. + * @tx_reg: physical address of data register used for + * memory-to-peripheral transfers + * @rx_reg: physical address of data register used for + * peripheral-to-memory transfers + * @reg_width: peripheral register width + * + * If dma_dev is non-NULL, the client can not be bound to other DMA + * masters than the one corresponding to this device. The DMA master + * driver may use this to determine if there is controller-specific + * data wrapped around this struct. Drivers of platform code that sets + * the dma_dev field must therefore make sure to use an appropriate + * controller-specific dma slave structure wrapping this struct. + */ +struct dma_slave { + struct device *dev; + struct device *dma_dev; + dma_addr_t tx_reg; + dma_addr_t rx_reg; + enum dma_slave_width reg_width; +}; + /** * struct dma_chan_percpu - the per-CPU part of struct dma_chan * @refcount: local_t used for open-coded "bigref" counting @@ -208,11 +247,14 @@ typedef enum dma_state_client (*dma_event_callback) (struct dma_client *client, * @event_callback: func ptr to call when something happens * @cap_mask: only return channels that satisfy the requested capabilities * a value of zero corresponds to any capability + * @slave: data for preparing slave transfer. Must be non-NULL iff the + * DMA_SLAVE capability is requested. * @global_node: list_head for global dma_client_list */ struct dma_client { dma_event_callback event_callback; dma_cap_mask_t cap_mask; + struct dma_slave *slave; struct list_head global_node; }; @@ -269,6 +311,8 @@ struct dma_async_tx_descriptor { * @device_prep_dma_zero_sum: prepares a zero_sum operation * @device_prep_dma_memset: prepares a memset operation * @device_prep_dma_interrupt: prepares an end of chain interrupt operation + * @device_prep_slave_sg: prepares a slave dma operation + * @device_terminate_all: terminate all pending operations * @device_issue_pending: push pending transactions to hardware */ struct dma_device { @@ -304,6 +348,12 @@ struct dma_device { struct dma_async_tx_descriptor *(*device_prep_dma_interrupt)( struct dma_chan *chan, unsigned long flags); + struct dma_async_tx_descriptor *(*device_prep_slave_sg)( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_data_direction direction, + unsigned long flags); + void (*device_terminate_all)(struct dma_chan *chan); + enum dma_status (*device_is_tx_complete)(struct dma_chan *chan, dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used); -- cgit From 3bfb1d20b547a5071d01344581eac5846ea84491 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 8 Jul 2008 11:59:42 -0700 Subject: dmaengine: Driver for the Synopsys DesignWare DMA controller This adds a driver for the Synopsys DesignWare DMA controller (aka DMACA on AVR32 systems.) This DMA controller can be found integrated on the AT32AP7000 chip and is primarily meant for peripheral DMA transfer, but can also be used for memory-to-memory transfers. This patch is based on a driver from David Brownell which was based on an older version of the DMA Engine framework. It also implements the proposed extensions to the DMA Engine API for slave DMA operations. The dmatest client shows no problems, but there may still be room for improvement performance-wise. DMA slave transfer performance is definitely "good enough"; reading 100 MiB from an SD card running at ~20 MHz yields ~7.2 MiB/s average transfer rate. Full documentation for this controller can be found in the Synopsys DW AHB DMAC Databook: http://www.synopsys.com/designware/docs/iip/DW_ahb_dmac/latest/doc/dw_ahb_dmac_db.pdf The controller has lots of implementation options, so it's usually a good idea to check the data sheet of the chip it's intergrated on as well. The AT32AP7000 data sheet can be found here: http://www.atmel.com/dyn/products/datasheets.asp?family_id=682 Changes since v4: * Use client_count instead of dma_chan_is_in_use() * Add missing include * Unmap buffers unless client told us not to Changes since v3: * Update to latest DMA engine and DMA slave APIs * Embed the hw descriptor into the sw descriptor * Clean up and update MODULE_DESCRIPTION, copyright date, etc. Changes since v2: * Dequeue all pending transfers in terminate_all() * Rename dw_dmac.h -> dw_dmac_regs.h * Define and use controller-specific dma_slave data * Fix up a few outdated comments * Define hardware registers as structs (doesn't generate better code, unfortunately, but it looks nicer.) * Get number of channels from platform_data instead of hardcoding it based on CONFIG_WHATEVER_CPU. * Give slave clients exclusive access to the channel Acked-by: Maciej Sosnowski , Signed-off-by: Haavard Skinnemoen Signed-off-by: Dan Williams --- arch/avr32/mach-at32ap/at32ap700x.c | 27 +- drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/dw_dmac.c | 1122 ++++++++++++++++++++++++++++ drivers/dma/dw_dmac_regs.h | 225 ++++++ include/asm-avr32/arch-at32ap/at32ap700x.h | 16 + include/linux/dw_dmac.h | 62 ++ 7 files changed, 1449 insertions(+), 13 deletions(-) create mode 100644 drivers/dma/dw_dmac.c create mode 100644 drivers/dma/dw_dmac_regs.h create mode 100644 include/linux/dw_dmac.h (limited to 'include/linux') diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 0f24b4f85c17..892e27e0d583 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -7,6 +7,7 @@ */ #include #include +#include #include #include #include @@ -599,6 +600,17 @@ static void __init genclk_init_parent(struct clk *clk) clk->parent = parent; } +static struct dw_dma_platform_data dw_dmac0_data = { + .nr_channels = 3, +}; + +static struct resource dw_dmac0_resource[] = { + PBMEM(0xff200000), + IRQ(2), +}; +DEFINE_DEV_DATA(dw_dmac, 0); +DEV_CLK(hclk, dw_dmac0, hsb, 10); + /* -------------------------------------------------------------------- * System peripherals * -------------------------------------------------------------------- */ @@ -705,17 +717,6 @@ static struct clk pico_clk = { .users = 1, }; -static struct resource dmaca0_resource[] = { - { - .start = 0xff200000, - .end = 0xff20ffff, - .flags = IORESOURCE_MEM, - }, - IRQ(2), -}; -DEFINE_DEV(dmaca, 0); -DEV_CLK(hclk, dmaca0, hsb, 10); - /* -------------------------------------------------------------------- * HMATRIX * -------------------------------------------------------------------- */ @@ -828,7 +829,7 @@ void __init at32_add_system_devices(void) platform_device_register(&at32_eic0_device); platform_device_register(&smc0_device); platform_device_register(&pdc_device); - platform_device_register(&dmaca0_device); + platform_device_register(&dw_dmac0_device); platform_device_register(&at32_tcb0_device); platform_device_register(&at32_tcb1_device); @@ -1891,7 +1892,7 @@ struct clk *at32_clock_list[] = { &smc0_mck, &pdc_hclk, &pdc_pclk, - &dmaca0_hclk, + &dw_dmac0_hclk, &pico_clk, &pio0_mck, &pio1_mck, diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 4b6bd3d099cf..cd303901eb5b 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -38,6 +38,15 @@ config INTEL_IOP_ADMA help Enable support for the Intel(R) IOP Series RAID engines. +config DW_DMAC + tristate "Synopsys DesignWare AHB DMA support" + depends on AVR32 + select DMA_ENGINE + default y if CPU_AT32AP7000 + help + Support the Synopsys DesignWare AHB DMA controller. This + can be integrated in chips such as the Atmel AT32ap7000. + config FSL_DMA bool "Freescale MPC85xx/MPC83xx DMA support" depends on PPC diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 181e3646fbfe..14f59527d4f6 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -6,3 +6,4 @@ ioatdma-objs := ioat.o ioat_dma.o ioat_dca.o obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_MV_XOR) += mv_xor.o +obj-$(CONFIG_DW_DMAC) += dw_dmac.o diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c new file mode 100644 index 000000000000..94df91771243 --- /dev/null +++ b/drivers/dma/dw_dmac.c @@ -0,0 +1,1122 @@ +/* + * Driver for the Synopsys DesignWare DMA Controller (aka DMACA on + * AVR32 systems.) + * + * Copyright (C) 2007-2008 Atmel Corporation + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dw_dmac_regs.h" + +/* + * This supports the Synopsys "DesignWare AHB Central DMA Controller", + * (DW_ahb_dmac) which is used with various AMBA 2.0 systems (not all + * of which use ARM any more). See the "Databook" from Synopsys for + * information beyond what licensees probably provide. + * + * The driver has currently been tested only with the Atmel AT32AP7000, + * which does not support descriptor writeback. + */ + +/* NOTE: DMS+SMS is system-specific. We should get this information + * from the platform code somehow. + */ +#define DWC_DEFAULT_CTLLO (DWC_CTLL_DST_MSIZE(0) \ + | DWC_CTLL_SRC_MSIZE(0) \ + | DWC_CTLL_DMS(0) \ + | DWC_CTLL_SMS(1) \ + | DWC_CTLL_LLP_D_EN \ + | DWC_CTLL_LLP_S_EN) + +/* + * This is configuration-dependent and usually a funny size like 4095. + * Let's round it down to the nearest power of two. + * + * Note that this is a transfer count, i.e. if we transfer 32-bit + * words, we can do 8192 bytes per descriptor. + * + * This parameter is also system-specific. + */ +#define DWC_MAX_COUNT 2048U + +/* + * Number of descriptors to allocate for each channel. This should be + * made configurable somehow; preferably, the clients (at least the + * ones using slave transfers) should be able to give us a hint. + */ +#define NR_DESCS_PER_CHANNEL 64 + +/*----------------------------------------------------------------------*/ + +/* + * Because we're not relying on writeback from the controller (it may not + * even be configured into the core!) we don't need to use dma_pool. These + * descriptors -- and associated data -- are cacheable. We do need to make + * sure their dcache entries are written back before handing them off to + * the controller, though. + */ + +static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) +{ + return list_entry(dwc->active_list.next, struct dw_desc, desc_node); +} + +static struct dw_desc *dwc_first_queued(struct dw_dma_chan *dwc) +{ + return list_entry(dwc->queue.next, struct dw_desc, desc_node); +} + +static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) +{ + struct dw_desc *desc, *_desc; + struct dw_desc *ret = NULL; + unsigned int i = 0; + + spin_lock_bh(&dwc->lock); + list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) { + if (async_tx_test_ack(&desc->txd)) { + list_del(&desc->desc_node); + ret = desc; + break; + } + dev_dbg(&dwc->chan.dev, "desc %p not ACKed\n", desc); + i++; + } + spin_unlock_bh(&dwc->lock); + + dev_vdbg(&dwc->chan.dev, "scanned %u descriptors on freelist\n", i); + + return ret; +} + +static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + struct dw_desc *child; + + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + dma_sync_single_for_cpu(dwc->chan.dev.parent, + child->txd.phys, sizeof(child->lli), + DMA_TO_DEVICE); + dma_sync_single_for_cpu(dwc->chan.dev.parent, + desc->txd.phys, sizeof(desc->lli), + DMA_TO_DEVICE); +} + +/* + * Move a descriptor, including any children, to the free list. + * `desc' must not be on any lists. + */ +static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + if (desc) { + struct dw_desc *child; + + dwc_sync_desc_for_cpu(dwc, desc); + + spin_lock_bh(&dwc->lock); + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + dev_vdbg(&dwc->chan.dev, + "moving child desc %p to freelist\n", + child); + list_splice_init(&desc->txd.tx_list, &dwc->free_list); + dev_vdbg(&dwc->chan.dev, "moving desc %p to freelist\n", desc); + list_add(&desc->desc_node, &dwc->free_list); + spin_unlock_bh(&dwc->lock); + } +} + +/* Called with dwc->lock held and bh disabled */ +static dma_cookie_t +dwc_assign_cookie(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + dma_cookie_t cookie = dwc->chan.cookie; + + if (++cookie < 0) + cookie = 1; + + dwc->chan.cookie = cookie; + desc->txd.cookie = cookie; + + return cookie; +} + +/*----------------------------------------------------------------------*/ + +/* Called with dwc->lock held and bh disabled */ +static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + + /* ASSERT: channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(&dwc->chan.dev, + "BUG: Attempted to start non-idle channel\n"); + dev_err(&dwc->chan.dev, + " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", + channel_readl(dwc, SAR), + channel_readl(dwc, DAR), + channel_readl(dwc, LLP), + channel_readl(dwc, CTL_HI), + channel_readl(dwc, CTL_LO)); + + /* The tasklet will hopefully advance the queue... */ + return; + } + + channel_writel(dwc, LLP, first->txd.phys); + channel_writel(dwc, CTL_LO, + DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); + channel_writel(dwc, CTL_HI, 0); + channel_set_bit(dw, CH_EN, dwc->mask); +} + +/*----------------------------------------------------------------------*/ + +static void +dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + dma_async_tx_callback callback; + void *param; + struct dma_async_tx_descriptor *txd = &desc->txd; + + dev_vdbg(&dwc->chan.dev, "descriptor %u complete\n", txd->cookie); + + dwc->completed = txd->cookie; + callback = txd->callback; + param = txd->callback_param; + + dwc_sync_desc_for_cpu(dwc, desc); + list_splice_init(&txd->tx_list, &dwc->free_list); + list_move(&desc->desc_node, &dwc->free_list); + + /* + * We use dma_unmap_page() regardless of how the buffers were + * mapped before they were submitted... + */ + if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) + dma_unmap_page(dwc->chan.dev.parent, desc->lli.dar, desc->len, + DMA_FROM_DEVICE); + if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) + dma_unmap_page(dwc->chan.dev.parent, desc->lli.sar, desc->len, + DMA_TO_DEVICE); + + /* + * The API requires that no submissions are done from a + * callback, so we don't need to drop the lock here + */ + if (callback) + callback(param); +} + +static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(&dwc->chan.dev, + "BUG: XFER bit set, but channel not idle!\n"); + + /* Try to continue after resetting the channel... */ + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + } + + /* + * Submit queued descriptors ASAP, i.e. before we go through + * the completed ones. + */ + if (!list_empty(&dwc->queue)) + dwc_dostart(dwc, dwc_first_queued(dwc)); + list_splice_init(&dwc->active_list, &list); + list_splice_init(&dwc->queue, &dwc->active_list); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc); +} + +static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + dma_addr_t llp; + struct dw_desc *desc, *_desc; + struct dw_desc *child; + u32 status_xfer; + + /* + * Clear block interrupt flag before scanning so that we don't + * miss any, and read LLP before RAW_XFER to ensure it is + * valid if we decide to scan the list. + */ + dma_writel(dw, CLEAR.BLOCK, dwc->mask); + llp = channel_readl(dwc, LLP); + status_xfer = dma_readl(dw, RAW.XFER); + + if (status_xfer & dwc->mask) { + /* Everything we've submitted is done */ + dma_writel(dw, CLEAR.XFER, dwc->mask); + dwc_complete_all(dw, dwc); + return; + } + + dev_vdbg(&dwc->chan.dev, "scan_descriptors: llp=0x%x\n", llp); + + list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { + if (desc->lli.llp == llp) + /* This one is currently in progress */ + return; + + list_for_each_entry(child, &desc->txd.tx_list, desc_node) + if (child->lli.llp == llp) + /* Currently in progress */ + return; + + /* + * No descriptors so far seem to be in progress, i.e. + * this one must be done. + */ + dwc_descriptor_complete(dwc, desc); + } + + dev_err(&dwc->chan.dev, + "BUG: All descriptors done, but channel not idle!\n"); + + /* Try to continue after resetting the channel... */ + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + + if (!list_empty(&dwc->queue)) { + dwc_dostart(dwc, dwc_first_queued(dwc)); + list_splice_init(&dwc->queue, &dwc->active_list); + } +} + +static void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) +{ + dev_printk(KERN_CRIT, &dwc->chan.dev, + " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", + lli->sar, lli->dar, lli->llp, + lli->ctlhi, lli->ctllo); +} + +static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + struct dw_desc *bad_desc; + struct dw_desc *child; + + dwc_scan_descriptors(dw, dwc); + + /* + * The descriptor currently at the head of the active list is + * borked. Since we don't have any way to report errors, we'll + * just have to scream loudly and try to carry on. + */ + bad_desc = dwc_first_active(dwc); + list_del_init(&bad_desc->desc_node); + list_splice_init(&dwc->queue, dwc->active_list.prev); + + /* Clear the error flag and try to restart the controller */ + dma_writel(dw, CLEAR.ERROR, dwc->mask); + if (!list_empty(&dwc->active_list)) + dwc_dostart(dwc, dwc_first_active(dwc)); + + /* + * KERN_CRITICAL may seem harsh, but since this only happens + * when someone submits a bad physical address in a + * descriptor, we should consider ourselves lucky that the + * controller flagged an error instead of scribbling over + * random memory locations. + */ + dev_printk(KERN_CRIT, &dwc->chan.dev, + "Bad descriptor submitted for DMA!\n"); + dev_printk(KERN_CRIT, &dwc->chan.dev, + " cookie: %d\n", bad_desc->txd.cookie); + dwc_dump_lli(dwc, &bad_desc->lli); + list_for_each_entry(child, &bad_desc->txd.tx_list, desc_node) + dwc_dump_lli(dwc, &child->lli); + + /* Pretend the descriptor completed successfully */ + dwc_descriptor_complete(dwc, bad_desc); +} + +static void dw_dma_tasklet(unsigned long data) +{ + struct dw_dma *dw = (struct dw_dma *)data; + struct dw_dma_chan *dwc; + u32 status_block; + u32 status_xfer; + u32 status_err; + int i; + + status_block = dma_readl(dw, RAW.BLOCK); + status_xfer = dma_readl(dw, RAW.BLOCK); + status_err = dma_readl(dw, RAW.ERROR); + + dev_vdbg(dw->dma.dev, "tasklet: status_block=%x status_err=%x\n", + status_block, status_err); + + for (i = 0; i < dw->dma.chancnt; i++) { + dwc = &dw->chan[i]; + spin_lock(&dwc->lock); + if (status_err & (1 << i)) + dwc_handle_error(dw, dwc); + else if ((status_block | status_xfer) & (1 << i)) + dwc_scan_descriptors(dw, dwc); + spin_unlock(&dwc->lock); + } + + /* + * Re-enable interrupts. Block Complete interrupts are only + * enabled if the INT_EN bit in the descriptor is set. This + * will trigger a scan before the whole list is done. + */ + channel_set_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_set_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_set_bit(dw, MASK.ERROR, dw->all_chan_mask); +} + +static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) +{ + struct dw_dma *dw = dev_id; + u32 status; + + dev_vdbg(dw->dma.dev, "interrupt: status=0x%x\n", + dma_readl(dw, STATUS_INT)); + + /* + * Just disable the interrupts. We'll turn them back on in the + * softirq handler. + */ + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + status = dma_readl(dw, STATUS_INT); + if (status) { + dev_err(dw->dma.dev, + "BUG: Unexpected interrupts pending: 0x%x\n", + status); + + /* Try to recover */ + channel_clear_bit(dw, MASK.XFER, (1 << 8) - 1); + channel_clear_bit(dw, MASK.BLOCK, (1 << 8) - 1); + channel_clear_bit(dw, MASK.SRC_TRAN, (1 << 8) - 1); + channel_clear_bit(dw, MASK.DST_TRAN, (1 << 8) - 1); + channel_clear_bit(dw, MASK.ERROR, (1 << 8) - 1); + } + + tasklet_schedule(&dw->tasklet); + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct dw_desc *desc = txd_to_dw_desc(tx); + struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan); + dma_cookie_t cookie; + + spin_lock_bh(&dwc->lock); + cookie = dwc_assign_cookie(dwc, desc); + + /* + * REVISIT: We should attempt to chain as many descriptors as + * possible, perhaps even appending to those already submitted + * for DMA. But this is hard to do in a race-free manner. + */ + if (list_empty(&dwc->active_list)) { + dev_vdbg(&tx->chan->dev, "tx_submit: started %u\n", + desc->txd.cookie); + dwc_dostart(dwc, desc); + list_add_tail(&desc->desc_node, &dwc->active_list); + } else { + dev_vdbg(&tx->chan->dev, "tx_submit: queued %u\n", + desc->txd.cookie); + + list_add_tail(&desc->desc_node, &dwc->queue); + } + + spin_unlock_bh(&dwc->lock); + + return cookie; +} + +static struct dma_async_tx_descriptor * +dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_desc *desc; + struct dw_desc *first; + struct dw_desc *prev; + size_t xfer_count; + size_t offset; + unsigned int src_width; + unsigned int dst_width; + u32 ctllo; + + dev_vdbg(&chan->dev, "prep_dma_memcpy d0x%x s0x%x l0x%zx f0x%lx\n", + dest, src, len, flags); + + if (unlikely(!len)) { + dev_dbg(&chan->dev, "prep_dma_memcpy: length is zero!\n"); + return NULL; + } + + /* + * We can be a lot more clever here, but this should take care + * of the most common optimization. + */ + if (!((src | dest | len) & 3)) + src_width = dst_width = 2; + else if (!((src | dest | len) & 1)) + src_width = dst_width = 1; + else + src_width = dst_width = 0; + + ctllo = DWC_DEFAULT_CTLLO + | DWC_CTLL_DST_WIDTH(dst_width) + | DWC_CTLL_SRC_WIDTH(src_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_INC + | DWC_CTLL_FC_M2M; + prev = first = NULL; + + for (offset = 0; offset < len; offset += xfer_count << src_width) { + xfer_count = min_t(size_t, (len - offset) >> src_width, + DWC_MAX_COUNT); + + desc = dwc_desc_get(dwc); + if (!desc) + goto err_desc_get; + + desc->lli.sar = src + offset; + desc->lli.dar = dest + offset; + desc->lli.ctllo = ctllo; + desc->lli.ctlhi = xfer_count; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, sizeof(prev->lli), + DMA_TO_DEVICE); + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + } + + + if (flags & DMA_PREP_INTERRUPT) + /* Trigger interrupt after last block */ + prev->lli.ctllo |= DWC_CTLL_INT_EN; + + prev->lli.llp = 0; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, sizeof(prev->lli), + DMA_TO_DEVICE); + + first->txd.flags = flags; + first->len = len; + + return &first->txd; + +err_desc_get: + dwc_desc_put(dwc, first); + return NULL; +} + +static struct dma_async_tx_descriptor * +dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_data_direction direction, + unsigned long flags) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma_slave *dws = dwc->dws; + struct dw_desc *prev; + struct dw_desc *first; + u32 ctllo; + dma_addr_t reg; + unsigned int reg_width; + unsigned int mem_width; + unsigned int i; + struct scatterlist *sg; + size_t total_len = 0; + + dev_vdbg(&chan->dev, "prep_dma_slave\n"); + + if (unlikely(!dws || !sg_len)) + return NULL; + + reg_width = dws->slave.reg_width; + prev = first = NULL; + + sg_len = dma_map_sg(chan->dev.parent, sgl, sg_len, direction); + + switch (direction) { + case DMA_TO_DEVICE: + ctllo = (DWC_DEFAULT_CTLLO + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_DST_FIX + | DWC_CTLL_SRC_INC + | DWC_CTLL_FC_M2P); + reg = dws->slave.tx_reg; + for_each_sg(sgl, sg, sg_len, i) { + struct dw_desc *desc; + u32 len; + u32 mem; + + desc = dwc_desc_get(dwc); + if (!desc) { + dev_err(&chan->dev, + "not enough descriptors available\n"); + goto err_desc_get; + } + + mem = sg_phys(sg); + len = sg_dma_len(sg); + mem_width = 2; + if (unlikely(mem & 3 || len & 3)) + mem_width = 0; + + desc->lli.sar = mem; + desc->lli.dar = reg; + desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); + desc->lli.ctlhi = len >> mem_width; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, + sizeof(prev->lli), + DMA_TO_DEVICE); + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + total_len += len; + } + break; + case DMA_FROM_DEVICE: + ctllo = (DWC_DEFAULT_CTLLO + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_FIX + | DWC_CTLL_FC_P2M); + + reg = dws->slave.rx_reg; + for_each_sg(sgl, sg, sg_len, i) { + struct dw_desc *desc; + u32 len; + u32 mem; + + desc = dwc_desc_get(dwc); + if (!desc) { + dev_err(&chan->dev, + "not enough descriptors available\n"); + goto err_desc_get; + } + + mem = sg_phys(sg); + len = sg_dma_len(sg); + mem_width = 2; + if (unlikely(mem & 3 || len & 3)) + mem_width = 0; + + desc->lli.sar = reg; + desc->lli.dar = mem; + desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); + desc->lli.ctlhi = len >> reg_width; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, + sizeof(prev->lli), + DMA_TO_DEVICE); + list_add_tail(&desc->desc_node, + &first->txd.tx_list); + } + prev = desc; + total_len += len; + } + break; + default: + return NULL; + } + + if (flags & DMA_PREP_INTERRUPT) + /* Trigger interrupt after last block */ + prev->lli.ctllo |= DWC_CTLL_INT_EN; + + prev->lli.llp = 0; + dma_sync_single_for_device(chan->dev.parent, + prev->txd.phys, sizeof(prev->lli), + DMA_TO_DEVICE); + + first->len = total_len; + + return &first->txd; + +err_desc_get: + dwc_desc_put(dwc, first); + return NULL; +} + +static void dwc_terminate_all(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + + /* + * This is only called when something went wrong elsewhere, so + * we don't really care about the data. Just disable the + * channel. We still have to poll the channel enable bit due + * to AHB/HSB limitations. + */ + spin_lock_bh(&dwc->lock); + + channel_clear_bit(dw, CH_EN, dwc->mask); + + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + + /* active_list entries will end up before queued entries */ + list_splice_init(&dwc->queue, &list); + list_splice_init(&dwc->active_list, &list); + + spin_unlock_bh(&dwc->lock); + + /* Flush all pending and queued descriptors */ + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc); +} + +static enum dma_status +dwc_is_tx_complete(struct dma_chan *chan, + dma_cookie_t cookie, + dma_cookie_t *done, dma_cookie_t *used) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + dma_cookie_t last_used; + dma_cookie_t last_complete; + int ret; + + last_complete = dwc->completed; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + if (ret != DMA_SUCCESS) { + dwc_scan_descriptors(to_dw_dma(chan->device), dwc); + + last_complete = dwc->completed; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + } + + if (done) + *done = last_complete; + if (used) + *used = last_used; + + return ret; +} + +static void dwc_issue_pending(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + + spin_lock_bh(&dwc->lock); + if (!list_empty(&dwc->queue)) + dwc_scan_descriptors(to_dw_dma(chan->device), dwc); + spin_unlock_bh(&dwc->lock); +} + +static int dwc_alloc_chan_resources(struct dma_chan *chan, + struct dma_client *client) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc; + struct dma_slave *slave; + struct dw_dma_slave *dws; + int i; + u32 cfghi; + u32 cfglo; + + dev_vdbg(&chan->dev, "alloc_chan_resources\n"); + + /* Channels doing slave DMA can only handle one client. */ + if (dwc->dws || client->slave) { + if (chan->client_count) + return -EBUSY; + } + + /* ASSERT: channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_dbg(&chan->dev, "DMA channel not idle?\n"); + return -EIO; + } + + dwc->completed = chan->cookie = 1; + + cfghi = DWC_CFGH_FIFO_MODE; + cfglo = 0; + + slave = client->slave; + if (slave) { + /* + * We need controller-specific data to set up slave + * transfers. + */ + BUG_ON(!slave->dma_dev || slave->dma_dev != dw->dma.dev); + + dws = container_of(slave, struct dw_dma_slave, slave); + + dwc->dws = dws; + cfghi = dws->cfg_hi; + cfglo = dws->cfg_lo; + } else { + dwc->dws = NULL; + } + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); + + /* + * NOTE: some controllers may have additional features that we + * need to initialize here, like "scatter-gather" (which + * doesn't mean what you think it means), and status writeback. + */ + + spin_lock_bh(&dwc->lock); + i = dwc->descs_allocated; + while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { + spin_unlock_bh(&dwc->lock); + + desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); + if (!desc) { + dev_info(&chan->dev, + "only allocated %d descriptors\n", i); + spin_lock_bh(&dwc->lock); + break; + } + + dma_async_tx_descriptor_init(&desc->txd, chan); + desc->txd.tx_submit = dwc_tx_submit; + desc->txd.flags = DMA_CTRL_ACK; + INIT_LIST_HEAD(&desc->txd.tx_list); + desc->txd.phys = dma_map_single(chan->dev.parent, &desc->lli, + sizeof(desc->lli), DMA_TO_DEVICE); + dwc_desc_put(dwc, desc); + + spin_lock_bh(&dwc->lock); + i = ++dwc->descs_allocated; + } + + /* Enable interrupts */ + channel_set_bit(dw, MASK.XFER, dwc->mask); + channel_set_bit(dw, MASK.BLOCK, dwc->mask); + channel_set_bit(dw, MASK.ERROR, dwc->mask); + + spin_unlock_bh(&dwc->lock); + + dev_dbg(&chan->dev, + "alloc_chan_resources allocated %d descriptors\n", i); + + return i; +} + +static void dwc_free_chan_resources(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + + dev_dbg(&chan->dev, "free_chan_resources (descs allocated=%u)\n", + dwc->descs_allocated); + + /* ASSERT: channel is idle */ + BUG_ON(!list_empty(&dwc->active_list)); + BUG_ON(!list_empty(&dwc->queue)); + BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask); + + spin_lock_bh(&dwc->lock); + list_splice_init(&dwc->free_list, &list); + dwc->descs_allocated = 0; + dwc->dws = NULL; + + /* Disable interrupts */ + channel_clear_bit(dw, MASK.XFER, dwc->mask); + channel_clear_bit(dw, MASK.BLOCK, dwc->mask); + channel_clear_bit(dw, MASK.ERROR, dwc->mask); + + spin_unlock_bh(&dwc->lock); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) { + dev_vdbg(&chan->dev, " freeing descriptor %p\n", desc); + dma_unmap_single(chan->dev.parent, desc->txd.phys, + sizeof(desc->lli), DMA_TO_DEVICE); + kfree(desc); + } + + dev_vdbg(&chan->dev, "free_chan_resources done\n"); +} + +/*----------------------------------------------------------------------*/ + +static void dw_dma_off(struct dw_dma *dw) +{ + dma_writel(dw, CFG, 0); + + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + while (dma_readl(dw, CFG) & DW_CFG_DMA_EN) + cpu_relax(); +} + +static int __init dw_probe(struct platform_device *pdev) +{ + struct dw_dma_platform_data *pdata; + struct resource *io; + struct dw_dma *dw; + size_t size; + int irq; + int err; + int i; + + pdata = pdev->dev.platform_data; + if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) + return -EINVAL; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!io) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + size = sizeof(struct dw_dma); + size += pdata->nr_channels * sizeof(struct dw_dma_chan); + dw = kzalloc(size, GFP_KERNEL); + if (!dw) + return -ENOMEM; + + if (!request_mem_region(io->start, DW_REGLEN, pdev->dev.driver->name)) { + err = -EBUSY; + goto err_kfree; + } + + memset(dw, 0, sizeof *dw); + + dw->regs = ioremap(io->start, DW_REGLEN); + if (!dw->regs) { + err = -ENOMEM; + goto err_release_r; + } + + dw->clk = clk_get(&pdev->dev, "hclk"); + if (IS_ERR(dw->clk)) { + err = PTR_ERR(dw->clk); + goto err_clk; + } + clk_enable(dw->clk); + + /* force dma off, just in case */ + dw_dma_off(dw); + + err = request_irq(irq, dw_dma_interrupt, 0, "dw_dmac", dw); + if (err) + goto err_irq; + + platform_set_drvdata(pdev, dw); + + tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); + + dw->all_chan_mask = (1 << pdata->nr_channels) - 1; + + INIT_LIST_HEAD(&dw->dma.channels); + for (i = 0; i < pdata->nr_channels; i++, dw->dma.chancnt++) { + struct dw_dma_chan *dwc = &dw->chan[i]; + + dwc->chan.device = &dw->dma; + dwc->chan.cookie = dwc->completed = 1; + dwc->chan.chan_id = i; + list_add_tail(&dwc->chan.device_node, &dw->dma.channels); + + dwc->ch_regs = &__dw_regs(dw)->CHAN[i]; + spin_lock_init(&dwc->lock); + dwc->mask = 1 << i; + + INIT_LIST_HEAD(&dwc->active_list); + INIT_LIST_HEAD(&dwc->queue); + INIT_LIST_HEAD(&dwc->free_list); + + channel_clear_bit(dw, CH_EN, dwc->mask); + } + + /* Clear/disable all interrupts on all channels. */ + dma_writel(dw, CLEAR.XFER, dw->all_chan_mask); + dma_writel(dw, CLEAR.BLOCK, dw->all_chan_mask); + dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask); + dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask); + dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask); + + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); + dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); + dw->dma.dev = &pdev->dev; + dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; + dw->dma.device_free_chan_resources = dwc_free_chan_resources; + + dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; + + dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; + dw->dma.device_terminate_all = dwc_terminate_all; + + dw->dma.device_is_tx_complete = dwc_is_tx_complete; + dw->dma.device_issue_pending = dwc_issue_pending; + + dma_writel(dw, CFG, DW_CFG_DMA_EN); + + printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n", + pdev->dev.bus_id, dw->dma.chancnt); + + dma_async_device_register(&dw->dma); + + return 0; + +err_irq: + clk_disable(dw->clk); + clk_put(dw->clk); +err_clk: + iounmap(dw->regs); + dw->regs = NULL; +err_release_r: + release_resource(io); +err_kfree: + kfree(dw); + return err; +} + +static int __exit dw_remove(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + struct dw_dma_chan *dwc, *_dwc; + struct resource *io; + + dw_dma_off(dw); + dma_async_device_unregister(&dw->dma); + + free_irq(platform_get_irq(pdev, 0), dw); + tasklet_kill(&dw->tasklet); + + list_for_each_entry_safe(dwc, _dwc, &dw->dma.channels, + chan.device_node) { + list_del(&dwc->chan.device_node); + channel_clear_bit(dw, CH_EN, dwc->mask); + } + + clk_disable(dw->clk); + clk_put(dw->clk); + + iounmap(dw->regs); + dw->regs = NULL; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(io->start, DW_REGLEN); + + kfree(dw); + + return 0; +} + +static void dw_shutdown(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + + dw_dma_off(platform_get_drvdata(pdev)); + clk_disable(dw->clk); +} + +static int dw_suspend_late(struct platform_device *pdev, pm_message_t mesg) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + + dw_dma_off(platform_get_drvdata(pdev)); + clk_disable(dw->clk); + return 0; +} + +static int dw_resume_early(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + + clk_enable(dw->clk); + dma_writel(dw, CFG, DW_CFG_DMA_EN); + return 0; + +} + +static struct platform_driver dw_driver = { + .remove = __exit_p(dw_remove), + .shutdown = dw_shutdown, + .suspend_late = dw_suspend_late, + .resume_early = dw_resume_early, + .driver = { + .name = "dw_dmac", + }, +}; + +static int __init dw_init(void) +{ + return platform_driver_probe(&dw_driver, dw_probe); +} +module_init(dw_init); + +static void __exit dw_exit(void) +{ + platform_driver_unregister(&dw_driver); +} +module_exit(dw_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver"); +MODULE_AUTHOR("Haavard Skinnemoen "); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h new file mode 100644 index 000000000000..00fdd187bb0c --- /dev/null +++ b/drivers/dma/dw_dmac_regs.h @@ -0,0 +1,225 @@ +/* + * Driver for the Synopsys DesignWare AHB DMA Controller + * + * Copyright (C) 2005-2007 Atmel Corporation + * + * 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 + +#define DW_DMA_MAX_NR_CHANNELS 8 + +/* + * Redefine this macro to handle differences between 32- and 64-bit + * addressing, big vs. little endian, etc. + */ +#define DW_REG(name) u32 name; u32 __pad_##name + +/* Hardware register definitions. */ +struct dw_dma_chan_regs { + DW_REG(SAR); /* Source Address Register */ + DW_REG(DAR); /* Destination Address Register */ + DW_REG(LLP); /* Linked List Pointer */ + u32 CTL_LO; /* Control Register Low */ + u32 CTL_HI; /* Control Register High */ + DW_REG(SSTAT); + DW_REG(DSTAT); + DW_REG(SSTATAR); + DW_REG(DSTATAR); + u32 CFG_LO; /* Configuration Register Low */ + u32 CFG_HI; /* Configuration Register High */ + DW_REG(SGR); + DW_REG(DSR); +}; + +struct dw_dma_irq_regs { + DW_REG(XFER); + DW_REG(BLOCK); + DW_REG(SRC_TRAN); + DW_REG(DST_TRAN); + DW_REG(ERROR); +}; + +struct dw_dma_regs { + /* per-channel registers */ + struct dw_dma_chan_regs CHAN[DW_DMA_MAX_NR_CHANNELS]; + + /* irq handling */ + struct dw_dma_irq_regs RAW; /* r */ + struct dw_dma_irq_regs STATUS; /* r (raw & mask) */ + struct dw_dma_irq_regs MASK; /* rw (set = irq enabled) */ + struct dw_dma_irq_regs CLEAR; /* w (ack, affects "raw") */ + + DW_REG(STATUS_INT); /* r */ + + /* software handshaking */ + DW_REG(REQ_SRC); + DW_REG(REQ_DST); + DW_REG(SGL_REQ_SRC); + DW_REG(SGL_REQ_DST); + DW_REG(LAST_SRC); + DW_REG(LAST_DST); + + /* miscellaneous */ + DW_REG(CFG); + DW_REG(CH_EN); + DW_REG(ID); + DW_REG(TEST); + + /* optional encoded params, 0x3c8..0x3 */ +}; + +/* Bitfields in CTL_LO */ +#define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ +#define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ +#define DWC_CTLL_SRC_WIDTH(n) ((n)<<4) +#define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */ +#define DWC_CTLL_DST_DEC (1<<7) +#define DWC_CTLL_DST_FIX (2<<7) +#define DWC_CTLL_SRC_INC (0<<7) /* SAR update/not */ +#define DWC_CTLL_SRC_DEC (1<<9) +#define DWC_CTLL_SRC_FIX (2<<9) +#define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */ +#define DWC_CTLL_SRC_MSIZE(n) ((n)<<14) +#define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */ +#define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */ +#define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */ +#define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */ +#define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */ +#define DWC_CTLL_FC_P2P (3 << 20) /* periph-to-periph */ +/* plus 4 transfer types for peripheral-as-flow-controller */ +#define DWC_CTLL_DMS(n) ((n)<<23) /* dst master select */ +#define DWC_CTLL_SMS(n) ((n)<<25) /* src master select */ +#define DWC_CTLL_LLP_D_EN (1 << 27) /* dest block chain */ +#define DWC_CTLL_LLP_S_EN (1 << 28) /* src block chain */ + +/* Bitfields in CTL_HI */ +#define DWC_CTLH_DONE 0x00001000 +#define DWC_CTLH_BLOCK_TS_MASK 0x00000fff + +/* Bitfields in CFG_LO. Platform-configurable bits are in */ +#define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ +#define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ +#define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ +#define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */ +#define DWC_CFGL_MAX_BURST(x) ((x) << 20) +#define DWC_CFGL_RELOAD_SAR (1 << 30) +#define DWC_CFGL_RELOAD_DAR (1 << 31) + +/* Bitfields in CFG_HI. Platform-configurable bits are in */ +#define DWC_CFGH_DS_UPD_EN (1 << 5) +#define DWC_CFGH_SS_UPD_EN (1 << 6) + +/* Bitfields in SGR */ +#define DWC_SGR_SGI(x) ((x) << 0) +#define DWC_SGR_SGC(x) ((x) << 20) + +/* Bitfields in DSR */ +#define DWC_DSR_DSI(x) ((x) << 0) +#define DWC_DSR_DSC(x) ((x) << 20) + +/* Bitfields in CFG */ +#define DW_CFG_DMA_EN (1 << 0) + +#define DW_REGLEN 0x400 + +struct dw_dma_chan { + struct dma_chan chan; + void __iomem *ch_regs; + u8 mask; + + spinlock_t lock; + + /* these other elements are all protected by lock */ + dma_cookie_t completed; + struct list_head active_list; + struct list_head queue; + struct list_head free_list; + + struct dw_dma_slave *dws; + + unsigned int descs_allocated; +}; + +static inline struct dw_dma_chan_regs __iomem * +__dwc_regs(struct dw_dma_chan *dwc) +{ + return dwc->ch_regs; +} + +#define channel_readl(dwc, name) \ + __raw_readl(&(__dwc_regs(dwc)->name)) +#define channel_writel(dwc, name, val) \ + __raw_writel((val), &(__dwc_regs(dwc)->name)) + +static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct dw_dma_chan, chan); +} + + +struct dw_dma { + struct dma_device dma; + void __iomem *regs; + struct tasklet_struct tasklet; + struct clk *clk; + + u8 all_chan_mask; + + struct dw_dma_chan chan[0]; +}; + +static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) +{ + return dw->regs; +} + +#define dma_readl(dw, name) \ + __raw_readl(&(__dw_regs(dw)->name)) +#define dma_writel(dw, name, val) \ + __raw_writel((val), &(__dw_regs(dw)->name)) + +#define channel_set_bit(dw, reg, mask) \ + dma_writel(dw, reg, ((mask) << 8) | (mask)) +#define channel_clear_bit(dw, reg, mask) \ + dma_writel(dw, reg, ((mask) << 8) | 0) + +static inline struct dw_dma *to_dw_dma(struct dma_device *ddev) +{ + return container_of(ddev, struct dw_dma, dma); +} + +/* LLI == Linked List Item; a.k.a. DMA block descriptor */ +struct dw_lli { + /* values that are not changed by hardware */ + dma_addr_t sar; + dma_addr_t dar; + dma_addr_t llp; /* chain to next lli */ + u32 ctllo; + /* values that may get written back: */ + u32 ctlhi; + /* sstat and dstat can snapshot peripheral register state. + * silicon config may discard either or both... + */ + u32 sstat; + u32 dstat; +}; + +struct dw_desc { + /* FIRST values the hardware uses */ + struct dw_lli lli; + + /* THEN values for driver housekeeping */ + struct list_head desc_node; + struct dma_async_tx_descriptor txd; + size_t len; +}; + +static inline struct dw_desc * +txd_to_dw_desc(struct dma_async_tx_descriptor *txd) +{ + return container_of(txd, struct dw_desc, txd); +} diff --git a/include/asm-avr32/arch-at32ap/at32ap700x.h b/include/asm-avr32/arch-at32ap/at32ap700x.h index 31e48b0e7324..d18a3053be0d 100644 --- a/include/asm-avr32/arch-at32ap/at32ap700x.h +++ b/include/asm-avr32/arch-at32ap/at32ap700x.h @@ -30,4 +30,20 @@ #define GPIO_PIN_PD(N) (GPIO_PIOD_BASE + (N)) #define GPIO_PIN_PE(N) (GPIO_PIOE_BASE + (N)) + +/* + * DMAC peripheral hardware handshaking interfaces, used with dw_dmac + */ +#define DMAC_MCI_RX 0 +#define DMAC_MCI_TX 1 +#define DMAC_DAC_TX 2 +#define DMAC_AC97_A_RX 3 +#define DMAC_AC97_A_TX 4 +#define DMAC_AC97_B_RX 5 +#define DMAC_AC97_B_TX 6 +#define DMAC_DMAREQ_0 7 +#define DMAC_DMAREQ_1 8 +#define DMAC_DMAREQ_2 9 +#define DMAC_DMAREQ_3 10 + #endif /* __ASM_ARCH_AT32AP700X_H__ */ diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h new file mode 100644 index 000000000000..04d217b442bf --- /dev/null +++ b/include/linux/dw_dmac.h @@ -0,0 +1,62 @@ +/* + * Driver for the Synopsys DesignWare DMA Controller (aka DMACA on + * AVR32 systems.) + * + * Copyright (C) 2007 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef DW_DMAC_H +#define DW_DMAC_H + +#include + +/** + * struct dw_dma_platform_data - Controller configuration parameters + * @nr_channels: Number of channels supported by hardware (max 8) + */ +struct dw_dma_platform_data { + unsigned int nr_channels; +}; + +/** + * struct dw_dma_slave - Controller-specific information about a slave + * @slave: Generic information about the slave + * @ctl_lo: Platform-specific initializer for the CTL_LO register + * @cfg_hi: Platform-specific initializer for the CFG_HI register + * @cfg_lo: Platform-specific initializer for the CFG_LO register + */ +struct dw_dma_slave { + struct dma_slave slave; + u32 cfg_hi; + u32 cfg_lo; +}; + +/* Platform-configurable bits in CFG_HI */ +#define DWC_CFGH_FCMODE (1 << 0) +#define DWC_CFGH_FIFO_MODE (1 << 1) +#define DWC_CFGH_PROTCTL(x) ((x) << 2) +#define DWC_CFGH_SRC_PER(x) ((x) << 7) +#define DWC_CFGH_DST_PER(x) ((x) << 11) + +/* Platform-configurable bits in CFG_LO */ +#define DWC_CFGL_PRIO(x) ((x) << 5) /* priority */ +#define DWC_CFGL_LOCK_CH_XFER (0 << 12) /* scope of LOCK_CH */ +#define DWC_CFGL_LOCK_CH_BLOCK (1 << 12) +#define DWC_CFGL_LOCK_CH_XACT (2 << 12) +#define DWC_CFGL_LOCK_BUS_XFER (0 << 14) /* scope of LOCK_BUS */ +#define DWC_CFGL_LOCK_BUS_BLOCK (1 << 14) +#define DWC_CFGL_LOCK_BUS_XACT (2 << 14) +#define DWC_CFGL_LOCK_CH (1 << 15) /* channel lockout */ +#define DWC_CFGL_LOCK_BUS (1 << 16) /* busmaster lockout */ +#define DWC_CFGL_HS_DST_POL (1 << 18) /* dst handshake active low */ +#define DWC_CFGL_HS_SRC_POL (1 << 19) /* src handshake active low */ + +static inline struct dw_dma_slave *to_dw_dma_slave(struct dma_slave *slave) +{ + return container_of(slave, struct dw_dma_slave, slave); +} + +#endif /* DW_DMAC_H */ -- cgit From 0f420358e3a2abc028320ace7783e2e38cae77bf Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Fri, 11 Jul 2008 22:02:23 +1000 Subject: md: Turn rdev->sb_offset into a sector-based quantity. Rename it to sb_start to make sure all users have been converted. Signed-off-by: Andre Noll Signed-off-by: Neil Brown --- drivers/md/bitmap.c | 10 +++--- drivers/md/md.c | 81 ++++++++++++++++++++++------------------------- include/linux/raid/md_k.h | 2 +- 3 files changed, 44 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index dedba16d42f7..eba83e25b678 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -225,7 +225,7 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde || test_bit(Faulty, &rdev->flags)) continue; - target = (rdev->sb_offset << 1) + offset + index * (PAGE_SIZE/512); + target = rdev->sb_start + offset + index * (PAGE_SIZE/512); if (sync_page_io(rdev->bdev, target, PAGE_SIZE, page, READ)) { page->index = index; @@ -262,12 +262,12 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) /* bitmap runs in to metadata */ return -EINVAL; if (rdev->data_offset + mddev->size*2 - > rdev->sb_offset*2 + bitmap->offset) + > rdev->sb_start + bitmap->offset) /* data runs in to bitmap */ return -EINVAL; - } else if (rdev->sb_offset*2 < rdev->data_offset) { + } else if (rdev->sb_start < rdev->data_offset) { /* METADATA BITMAP DATA */ - if (rdev->sb_offset*2 + if (rdev->sb_start + bitmap->offset + page->index*(PAGE_SIZE/512) + size/512 > rdev->data_offset) @@ -277,7 +277,7 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) /* DATA METADATA BITMAP - no problems */ } md_super_write(mddev, rdev, - (rdev->sb_offset<<1) + bitmap->offset + rdev->sb_start + bitmap->offset + page->index * (PAGE_SIZE/512), size, page); diff --git a/drivers/md/md.c b/drivers/md/md.c index 3276edde7576..5590cb54b584 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -356,7 +356,7 @@ static inline sector_t calc_dev_sboffset(struct block_device *bdev) static sector_t calc_num_sectors(mdk_rdev_t *rdev, unsigned chunk_size) { - sector_t num_sectors = rdev->sb_offset * 2; + sector_t num_sectors = rdev->sb_start; if (chunk_size) num_sectors &= ~((sector_t)chunk_size/512 - 1); @@ -383,7 +383,7 @@ static void free_disk_sb(mdk_rdev_t * rdev) put_page(rdev->sb_page); rdev->sb_loaded = 0; rdev->sb_page = NULL; - rdev->sb_offset = 0; + rdev->sb_start = 0; rdev->size = 0; } } @@ -529,7 +529,7 @@ static int read_disk_sb(mdk_rdev_t * rdev, int size) return 0; - if (!sync_page_io(rdev->bdev, rdev->sb_offset<<1, size, rdev->sb_page, READ)) + if (!sync_page_io(rdev->bdev, rdev->sb_start, size, rdev->sb_page, READ)) goto fail; rdev->sb_loaded = 1; return 0; @@ -666,16 +666,14 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; mdp_super_t *sb; int ret; - sector_t sb_offset; /* - * Calculate the position of the superblock, + * Calculate the position of the superblock (512byte sectors), * it's at the end of the disk. * * It also happens to be a multiple of 4Kb. */ - sb_offset = calc_dev_sboffset(rdev->bdev) / 2; - rdev->sb_offset = sb_offset; + rdev->sb_start = calc_dev_sboffset(rdev->bdev); ret = read_disk_sb(rdev, MD_SB_BYTES); if (ret) return ret; @@ -1007,10 +1005,10 @@ super_90_rdev_size_change(mdk_rdev_t *rdev, unsigned long long size) size *= 2; /* convert to sectors */ if (rdev->mddev->bitmap_offset) return 0; /* can't move bitmap */ - rdev->sb_offset = calc_dev_sboffset(rdev->bdev) / 2; - if (!size || size > rdev->sb_offset*2) - size = rdev->sb_offset*2; - md_super_write(rdev->mddev, rdev, rdev->sb_offset << 1, rdev->sb_size, + rdev->sb_start = calc_dev_sboffset(rdev->bdev); + if (!size || size > rdev->sb_start) + size = rdev->sb_start; + md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size, rdev->sb_page); md_super_wait(rdev->mddev); return size/2; /* kB for sysfs */ @@ -1048,12 +1046,12 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) { struct mdp_superblock_1 *sb; int ret; - sector_t sb_offset; + sector_t sb_start; char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; int bmask; /* - * Calculate the position of the superblock. + * Calculate the position of the superblock in 512byte sectors. * It is always aligned to a 4K boundary and * depeding on minor_version, it can be: * 0: At least 8K, but less than 12K, from end of device @@ -1062,22 +1060,20 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) */ switch(minor_version) { case 0: - sb_offset = rdev->bdev->bd_inode->i_size >> 9; - sb_offset -= 8*2; - sb_offset &= ~(sector_t)(4*2-1); - /* convert from sectors to K */ - sb_offset /= 2; + sb_start = rdev->bdev->bd_inode->i_size >> 9; + sb_start -= 8*2; + sb_start &= ~(sector_t)(4*2-1); break; case 1: - sb_offset = 0; + sb_start = 0; break; case 2: - sb_offset = 4; + sb_start = 8; break; default: return -EINVAL; } - rdev->sb_offset = sb_offset; + rdev->sb_start = sb_start; /* superblock is rarely larger than 1K, but it can be larger, * and it is safe to read 4k, so we do that @@ -1091,7 +1087,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) if (sb->magic != cpu_to_le32(MD_SB_MAGIC) || sb->major_version != cpu_to_le32(1) || le32_to_cpu(sb->max_dev) > (4096-256)/2 || - le64_to_cpu(sb->super_offset) != (rdev->sb_offset<<1) || + le64_to_cpu(sb->super_offset) != rdev->sb_start || (le32_to_cpu(sb->feature_map) & ~MD_FEATURE_ALL) != 0) return -EINVAL; @@ -1127,7 +1123,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) rdev->sb_size = (rdev->sb_size | bmask) + 1; if (minor_version - && rdev->data_offset < sb_offset + (rdev->sb_size/512)) + && rdev->data_offset < sb_start + (rdev->sb_size/512)) return -EINVAL; if (sb->level == cpu_to_le32(LEVEL_MULTIPATH)) @@ -1163,7 +1159,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) if (minor_version) rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2; else - rdev->size = rdev->sb_offset; + rdev->size = rdev->sb_start / 2; if (rdev->size < le64_to_cpu(sb->data_size)/2) return -EINVAL; rdev->size = le64_to_cpu(sb->data_size)/2; @@ -1350,7 +1346,7 @@ super_1_rdev_size_change(mdk_rdev_t *rdev, unsigned long long size) if (size && size < rdev->mddev->size) return 0; /* component must fit device */ size *= 2; /* convert to sectors */ - if (rdev->sb_offset < rdev->data_offset/2) { + if (rdev->sb_start < rdev->data_offset) { /* minor versions 1 and 2; superblock before data */ max_size = (rdev->bdev->bd_inode->i_size >> 9); max_size -= rdev->data_offset; @@ -1361,19 +1357,19 @@ super_1_rdev_size_change(mdk_rdev_t *rdev, unsigned long long size) return 0; } else { /* minor version 0; superblock after data */ - sector_t sb_offset; - sb_offset = (rdev->bdev->bd_inode->i_size >> 9) - 8*2; - sb_offset &= ~(sector_t)(4*2 - 1); - max_size = rdev->size*2 + sb_offset - rdev->sb_offset*2; + sector_t sb_start; + sb_start = (rdev->bdev->bd_inode->i_size >> 9) - 8*2; + sb_start &= ~(sector_t)(4*2 - 1); + max_size = rdev->size*2 + sb_start - rdev->sb_start; if (!size || size > max_size) size = max_size; - rdev->sb_offset = sb_offset/2; + rdev->sb_start = sb_start; } sb = (struct mdp_superblock_1 *) page_address(rdev->sb_page); sb->data_size = cpu_to_le64(size); - sb->super_offset = rdev->sb_offset*2; + sb->super_offset = rdev->sb_start; sb->sb_csum = calc_sb_1_csum(sb); - md_super_write(rdev->mddev, rdev, rdev->sb_offset << 1, rdev->sb_size, + md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size, rdev->sb_page); md_super_wait(rdev->mddev); return size/2; /* kB for sysfs */ @@ -1810,11 +1806,11 @@ repeat: dprintk("%s ", bdevname(rdev->bdev,b)); if (!test_bit(Faulty, &rdev->flags)) { md_super_write(mddev,rdev, - rdev->sb_offset<<1, rdev->sb_size, + rdev->sb_start, rdev->sb_size, rdev->sb_page); dprintk(KERN_INFO "(write) %s's sb offset: %llu\n", bdevname(rdev->bdev,b), - (unsigned long long)rdev->sb_offset); + (unsigned long long)rdev->sb_start); rdev->sb_events = mddev->events; } else @@ -3577,16 +3573,16 @@ static int do_md_run(mddev_t * mddev) * We don't want the data to overlap the metadata, * Internal Bitmap issues has handled elsewhere. */ - if (rdev->data_offset < rdev->sb_offset) { + if (rdev->data_offset < rdev->sb_start) { if (mddev->size && rdev->data_offset + mddev->size*2 - > rdev->sb_offset*2) { + > rdev->sb_start) { printk("md: %s: data overlaps metadata\n", mdname(mddev)); return -EINVAL; } } else { - if (rdev->sb_offset*2 + rdev->sb_size/512 + if (rdev->sb_start + rdev->sb_size/512 > rdev->data_offset) { printk("md: %s: metadata overlaps data\n", mdname(mddev)); @@ -4355,9 +4351,9 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) if (!mddev->persistent) { printk(KERN_INFO "md: nonpersistent superblock ...\n"); - rdev->sb_offset = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + rdev->sb_start = rdev->bdev->bd_inode->i_size / 512; } else - rdev->sb_offset = calc_dev_sboffset(rdev->bdev) / 2; + rdev->sb_start = calc_dev_sboffset(rdev->bdev); rdev->size = calc_num_sectors(rdev, mddev->chunk_size) / 2; err = bind_rdev_to_array(rdev, mddev); @@ -4424,10 +4420,9 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) } if (mddev->persistent) - rdev->sb_offset = calc_dev_sboffset(rdev->bdev) / 2; + rdev->sb_start = calc_dev_sboffset(rdev->bdev); else - rdev->sb_offset = - rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + rdev->sb_start = rdev->bdev->bd_inode->i_size / 512; rdev->size = calc_num_sectors(rdev, mddev->chunk_size) / 2; @@ -4628,7 +4623,7 @@ static int update_size(mddev_t *mddev, sector_t num_sectors) * linear and raid0 always use whatever space is available. We can only * consider changing this number if no resync or reconstruction is * happening, and if the new size is acceptable. It must fit before the - * sb_offset or, if that is Date: Fri, 11 Jul 2008 22:02:23 +1000 Subject: md: Remove some unused macros. Signed-off-by: Andre Noll Signed-off-by: Neil Brown --- include/linux/raid/md_p.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/raid/md_p.h b/include/linux/raid/md_p.h index 3f2cd98c508b..8b4de4a41ff1 100644 --- a/include/linux/raid/md_p.h +++ b/include/linux/raid/md_p.h @@ -43,14 +43,11 @@ */ #define MD_RESERVED_BYTES (64 * 1024) #define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512) -#define MD_RESERVED_BLOCKS (MD_RESERVED_BYTES / BLOCK_SIZE) #define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS) -#define MD_NEW_SIZE_BLOCKS(x) ((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS) #define MD_SB_BYTES 4096 #define MD_SB_WORDS (MD_SB_BYTES / 4) -#define MD_SB_BLOCKS (MD_SB_BYTES / BLOCK_SIZE) #define MD_SB_SECTORS (MD_SB_BYTES / 512) /* -- cgit From d280eadc4fba0bf99fb1c3b60e8c5e007f7da02c Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 6 Jun 2008 17:13:02 +0800 Subject: [NET] smc91x: remove "irq_flags" from "struct smc91x_platdata" IRQ trigger type can be specified in the IRQ resource definition by IORESOURCE_IRQ_*, we need only one way to specify this. This also fixes the following small issue: To allow dynamic support for multiple platforms, when those relevant macros are not defined for one specific platform, the default case will be: - SMC_DYNAMIC_BUS_CONFIG defined - and SMC_IRQ_FLAGS = IRQF_TRIGGER_RISING While if "irq_flags" is missing when defining the smc91x_platdata, usually as follows: static struct smc91x_platdata xxxx_smc91x_data = { .flags = SMC91X_USE_XXBIT, }; The lp->cfg.irq_flags will always be overriden by the above structure (due to a memcpy), thus rendering lp->cfg.irq_flags to be "0" always. (regardless of the default SMC_IRQ_FLAGS or IORESOURCE_IRQ_* flags) Fixes this by forcing to use IORESOURCE_IRQ_* flags if present, and make the only user of smc91x_platdata.irq_flags (renesas/migor) to use IORESOURCE_IRQ_*. Signed-off-by: Eric Miao Acked-by: Nicolas Pitre Acked-by: Jeff Garzik Signed-off-by: Russell King --- arch/sh/boards/renesas/migor/setup.c | 3 +-- drivers/net/smc91x.c | 9 +++++---- include/linux/smc91x.h | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/arch/sh/boards/renesas/migor/setup.c b/arch/sh/boards/renesas/migor/setup.c index 01af44245b57..963c99322095 100644 --- a/arch/sh/boards/renesas/migor/setup.c +++ b/arch/sh/boards/renesas/migor/setup.c @@ -30,7 +30,6 @@ static struct smc91x_platdata smc91x_info = { .flags = SMC91X_USE_16BIT, - .irq_flags = IRQF_TRIGGER_HIGH, }; static struct resource smc91x_eth_resources[] = { @@ -42,7 +41,7 @@ static struct resource smc91x_eth_resources[] = { }, [1] = { .start = 32, /* IRQ0 */ - .flags = IORESOURCE_IRQ, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, }, }; diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index f2051b209da2..e4a6c361995a 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -2123,6 +2123,7 @@ static int smc_drv_probe(struct platform_device *pdev) struct net_device *ndev; struct resource *res, *ires; unsigned int __iomem *addr; + unsigned long irq_flags = SMC_IRQ_FLAGS; int ret; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); @@ -2152,7 +2153,6 @@ static int smc_drv_probe(struct platform_device *pdev) */ lp = netdev_priv(ndev); - lp->cfg.irq_flags = SMC_IRQ_FLAGS; #ifdef SMC_DYNAMIC_BUS_CONFIG if (pd) @@ -2177,8 +2177,9 @@ static int smc_drv_probe(struct platform_device *pdev) } ndev->irq = ires->start; - if (SMC_IRQ_FLAGS == -1) - lp->cfg.irq_flags = ires->flags & IRQF_TRIGGER_MASK; + + if (ires->flags & IRQF_TRIGGER_MASK) + irq_flags = ires->flags & IRQF_TRIGGER_MASK; ret = smc_request_attrib(pdev); if (ret) @@ -2205,7 +2206,7 @@ static int smc_drv_probe(struct platform_device *pdev) } #endif - ret = smc_probe(ndev, addr, lp->cfg.irq_flags); + ret = smc_probe(ndev, addr, irq_flags); if (ret != 0) goto out_iounmap; diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index 8e0556b8781c..fc7682f04d89 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -7,7 +7,6 @@ struct smc91x_platdata { unsigned long flags; - unsigned long irq_flags; /* IRQF_... */ }; #endif /* __SMC91X_H__ */ -- cgit From c4f0e76747e80578a8f7fddd82fd0ce8127bd2f8 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Thu, 19 Jun 2008 17:39:03 +0800 Subject: [NET] smc91x: add SMC91X_NOWAIT flag to platform data And also favors the usage of SMC91X_NOWAIT over the hardcoded SMC_NOWAIT by converting "nowait" (module parameter overridable) to platform flag. There are several possibilities: 1. platform data present - preferred and use as is 2. platform data absent - use "nowait", it can be: a. SMC_NOWAIT if defined b. default to 0 if SMC_NOWAIT isn't defined c. overriden by module parameter Signed-off-by: Eric Miao Acked-by: Nicolas Pitre Acked-by: Jeff Garzik Signed-off-by: Russell King --- drivers/net/smc91x.c | 3 ++- include/linux/smc91x.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 1b19022b6c7c..de7a913c487c 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -308,7 +308,7 @@ static void smc_reset(struct net_device *dev) * can't handle it then there will be no recovery except for * a hard reset or power cycle */ - if (nowait) + if (lp->cfg.flags & SMC91X_NOWAIT) cfg |= CONFIG_NO_WAIT; /* @@ -2160,6 +2160,7 @@ static int smc_drv_probe(struct platform_device *pdev) lp->cfg.flags |= (SMC_CAN_USE_8BIT) ? SMC91X_USE_8BIT : 0; lp->cfg.flags |= (SMC_CAN_USE_16BIT) ? SMC91X_USE_16BIT : 0; lp->cfg.flags |= (SMC_CAN_USE_32BIT) ? SMC91X_USE_32BIT : 0; + lp->cfg.flags |= (nowait) ? SMC91X_NOWAIT : 0; } ndev->dma = (unsigned char)-1; diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index fc7682f04d89..90434db72db2 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -5,6 +5,8 @@ #define SMC91X_USE_16BIT (1 << 1) #define SMC91X_USE_32BIT (1 << 2) +#define SMC91X_NOWAIT (1 << 3) + struct smc91x_platdata { unsigned long flags; }; -- cgit From 159198862adad7109bb347bb30a620f67beac45f Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Tue, 24 Jun 2008 13:38:50 +0800 Subject: [NET] smc91x: prepare for SMC_IO_SHIFT to be a platform configurable variable Now one can use the following code #define SMC_IO_SHIFT lp->io_shift to make SMC_IO_SHIFT a variable. This, however, will slightly increase the CPU overhead and have negative impact on the network performance. The tradeoff is, this can be specified in the smc91x platform data so that multiple boards support can be built in a single zImage. Signed-off-by: Eric Miao Acked-by: Nicolas Pitre Acked-by: Jeff Garzik Signed-off-by: Russell King --- drivers/net/smc91x.c | 57 +++++++++++++++++++++++++++----------------------- drivers/net/smc91x.h | 3 +++ include/linux/smc91x.h | 7 +++++++ 3 files changed, 41 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index de7a913c487c..34bfc60e8074 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -2050,9 +2050,11 @@ static int smc_enable_device(struct platform_device *pdev) return 0; } -static int smc_request_attrib(struct platform_device *pdev) +static int smc_request_attrib(struct platform_device *pdev, + struct net_device *ndev) { struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); + struct smc_local *lp = netdev_priv(ndev); if (!res) return 0; @@ -2063,9 +2065,11 @@ static int smc_request_attrib(struct platform_device *pdev) return 0; } -static void smc_release_attrib(struct platform_device *pdev) +static void smc_release_attrib(struct platform_device *pdev, + struct net_device *ndev) { struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib"); + struct smc_local *lp = netdev_priv(ndev); if (res) release_mem_region(res->start, ATTRIB_SIZE); @@ -2126,25 +2130,11 @@ static int smc_drv_probe(struct platform_device *pdev) unsigned long irq_flags = SMC_IRQ_FLAGS; int ret; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); - if (!res) - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto out; - } - - - if (!request_mem_region(res->start, SMC_IO_EXTENT, CARDNAME)) { - ret = -EBUSY; - goto out; - } - ndev = alloc_etherdev(sizeof(struct smc_local)); if (!ndev) { printk("%s: could not allocate device.\n", CARDNAME); ret = -ENOMEM; - goto out_release_io; + goto out; } SET_NETDEV_DEV(ndev, &pdev->dev); @@ -2154,9 +2144,10 @@ static int smc_drv_probe(struct platform_device *pdev) lp = netdev_priv(ndev); - if (pd) + if (pd) { memcpy(&lp->cfg, pd, sizeof(lp->cfg)); - else { + lp->io_shift = SMC91X_IO_SHIFT(lp->cfg.flags); + } else { lp->cfg.flags |= (SMC_CAN_USE_8BIT) ? SMC91X_USE_8BIT : 0; lp->cfg.flags |= (SMC_CAN_USE_16BIT) ? SMC91X_USE_16BIT : 0; lp->cfg.flags |= (SMC_CAN_USE_32BIT) ? SMC91X_USE_32BIT : 0; @@ -2165,10 +2156,24 @@ static int smc_drv_probe(struct platform_device *pdev) ndev->dma = (unsigned char)-1; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto out_free_netdev; + } + + + if (!request_mem_region(res->start, SMC_IO_EXTENT, CARDNAME)) { + ret = -EBUSY; + goto out_free_netdev; + } + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!ires) { ret = -ENODEV; - goto out_free_netdev; + goto out_release_io; } ndev->irq = ires->start; @@ -2176,9 +2181,9 @@ static int smc_drv_probe(struct platform_device *pdev) if (ires->flags & IRQF_TRIGGER_MASK) irq_flags = ires->flags & IRQF_TRIGGER_MASK; - ret = smc_request_attrib(pdev); + ret = smc_request_attrib(pdev, ndev); if (ret) - goto out_free_netdev; + goto out_release_io; #if defined(CONFIG_SA1100_ASSABET) NCR_0 |= NCR_ENET_OSC_EN; #endif @@ -2213,11 +2218,11 @@ static int smc_drv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); iounmap(addr); out_release_attrib: - smc_release_attrib(pdev); - out_free_netdev: - free_netdev(ndev); + smc_release_attrib(pdev, ndev); out_release_io: release_mem_region(res->start, SMC_IO_EXTENT); + out_free_netdev: + free_netdev(ndev); out: printk("%s: not found (%d).\n", CARDNAME, ret); @@ -2243,7 +2248,7 @@ static int smc_drv_remove(struct platform_device *pdev) iounmap(lp->base); smc_release_datacs(pdev,ndev); - smc_release_attrib(pdev); + smc_release_attrib(pdev,ndev); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs"); if (!res) diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index 8310f1a073d8..80fb80f39200 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -500,6 +500,9 @@ struct smc_local { void __iomem *base; void __iomem *datacs; + /* the low address lines on some platforms aren't connected... */ + int io_shift; + struct smc91x_platdata cfg; }; diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index 90434db72db2..0dea9459a8e4 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -7,6 +7,13 @@ #define SMC91X_NOWAIT (1 << 3) +/* two bits for IO_SHIFT, let's hope later designs will keep this sane */ +#define SMC91X_IO_SHIFT_0 (0 << 4) +#define SMC91X_IO_SHIFT_1 (1 << 4) +#define SMC91X_IO_SHIFT_2 (2 << 4) +#define SMC91X_IO_SHIFT_3 (3 << 4) +#define SMC91X_IO_SHIFT(x) (((x) >> 4) & 0x3) + struct smc91x_platdata { unsigned long flags; }; -- cgit From 52256c0e06e4a4df67134b951a21b50c713a9588 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Tue, 24 Jun 2008 15:36:05 +0800 Subject: [NET] smc91x: prepare SMC_USE_PXA_DMA to be specified in platform data Now that the original SMC_USE_PXA_DMA specific code will always being built if CONFIG_ARCH_PXA is defined, so to make this part of the code to be PXA public, and still prevent it from being built if support of PXA is not selected. A SMC91X_USE_DMA flag is added to the platform data to allow platform to choose its usage of DMA. Note this flag itself is so named to be generic enough (assuming other platforms can also use DMA). It keeps backward compatibility to set the SMC91X_USE_DMA flag if SMC_USE_PXA_DMA is still defined. Signed-off-by: Eric Miao Acked-by: Nicolas Pitre Acked-by: Jeff Garzik Signed-off-by: Russell King --- drivers/net/smc91x.c | 13 ++++++++----- drivers/net/smc91x.h | 6 +++--- include/linux/smc91x.h | 2 ++ 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 34bfc60e8074..2040965d7724 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -1939,8 +1939,11 @@ static int __init smc_probe(struct net_device *dev, void __iomem *ioaddr, if (retval) goto err_out; -#ifdef SMC_USE_PXA_DMA - { +#ifdef CONFIG_ARCH_PXA +# ifdef SMC_USE_PXA_DMA + lp->cfg.flags |= SMC91X_USE_DMA; +# endif + if (lp->cfg.flags & SMC91X_USE_DMA) { int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW, smc_pxa_dma_irq, NULL); if (dma >= 0) @@ -1980,7 +1983,7 @@ static int __init smc_probe(struct net_device *dev, void __iomem *ioaddr, } err_out: -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA if (retval && dev->dma != (unsigned char)-1) pxa_free_dma(dev->dma); #endif @@ -2198,7 +2201,7 @@ static int smc_drv_probe(struct platform_device *pdev) goto out_release_attrib; } -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA { struct smc_local *lp = netdev_priv(ndev); lp->device = &pdev->dev; @@ -2241,7 +2244,7 @@ static int smc_drv_remove(struct platform_device *pdev) free_irq(ndev->irq, ndev); -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA if (ndev->dma != (unsigned char)-1) pxa_free_dma(ndev->dma); #endif diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index 80fb80f39200..f02cc6ac248b 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -492,7 +492,7 @@ struct smc_local { spinlock_t lock; -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA /* DMA needs the physical address of the chip */ u_long physaddr; struct device *device; @@ -510,7 +510,7 @@ struct smc_local { #define SMC_16BIT(p) ((p)->cfg.flags & SMC91X_USE_16BIT) #define SMC_32BIT(p) ((p)->cfg.flags & SMC91X_USE_32BIT) -#ifdef SMC_USE_PXA_DMA +#ifdef CONFIG_ARCH_PXA /* * Let's use the DMA engine on the XScale PXA2xx for RX packets. This is * always happening in irq context so no need to worry about races. TX is @@ -604,7 +604,7 @@ smc_pxa_dma_irq(int dma, void *dummy) { DCSR(dma) = 0; } -#endif /* SMC_USE_PXA_DMA */ +#endif /* CONFIG_ARCH_PXA */ /* diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h index 0dea9459a8e4..3827b922ba1f 100644 --- a/include/linux/smc91x.h +++ b/include/linux/smc91x.h @@ -14,6 +14,8 @@ #define SMC91X_IO_SHIFT_3 (3 << 4) #define SMC91X_IO_SHIFT(x) (((x) >> 4) & 0x3) +#define SMC91X_USE_DMA (1 << 6) + struct smc91x_platdata { unsigned long flags; }; -- cgit From d49747bdfb2ddebea24d1580da55b79d093d48a9 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 9 Oct 2007 12:37:13 -0500 Subject: powerpc/mpc83xx: Power Management support Basic PM support for 83xx. Standby is implemented as sleep. Suspend-to-RAM is implemented as "deep sleep" (with the processor turned off) on 831x. Signed-off-by: Scott Wood Signed-off-by: Kumar Gala --- arch/powerpc/Kconfig | 2 +- arch/powerpc/platforms/83xx/Makefile | 1 + arch/powerpc/platforms/83xx/suspend-asm.S | 533 ++++++++++++++++++++++++++++++ arch/powerpc/platforms/83xx/suspend.c | 388 ++++++++++++++++++++++ arch/powerpc/sysdev/fsl_soc.h | 1 + arch/powerpc/sysdev/ipic.c | 71 ++++ include/asm-powerpc/reg.h | 4 + include/linux/fsl_devices.h | 6 + 8 files changed, 1005 insertions(+), 1 deletion(-) create mode 100644 arch/powerpc/platforms/83xx/suspend-asm.S create mode 100644 arch/powerpc/platforms/83xx/suspend.c (limited to 'include/linux') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index de88972c5896..50be303e525d 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -199,7 +199,7 @@ config ARCH_HIBERNATION_POSSIBLE config ARCH_SUSPEND_POSSIBLE def_bool y - depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 + depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 || PPC_83xx config PPC_DCR_NATIVE bool diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile index f331fd7dd836..32c7ad13911c 100644 --- a/arch/powerpc/platforms/83xx/Makefile +++ b/arch/powerpc/platforms/83xx/Makefile @@ -3,6 +3,7 @@ # obj-y := misc.o usb.o obj-$(CONFIG_PCI) += pci.o +obj-$(CONFIG_SUSPEND) += suspend.o suspend-asm.o obj-$(CONFIG_MPC831x_RDB) += mpc831x_rdb.o obj-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.o obj-$(CONFIG_MPC834x_MDS) += mpc834x_mds.o diff --git a/arch/powerpc/platforms/83xx/suspend-asm.S b/arch/powerpc/platforms/83xx/suspend-asm.S new file mode 100644 index 000000000000..1930543c98d3 --- /dev/null +++ b/arch/powerpc/platforms/83xx/suspend-asm.S @@ -0,0 +1,533 @@ +/* + * Enter and leave deep sleep state on MPC83xx + * + * Copyright (c) 2006-2008 Freescale Semiconductor, Inc. + * Author: Scott Wood + * + * 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 +#include +#include +#include + +#define SS_MEMSAVE 0x00 /* First 8 bytes of RAM */ +#define SS_HID 0x08 /* 3 HIDs */ +#define SS_IABR 0x14 /* 2 IABRs */ +#define SS_IBCR 0x1c +#define SS_DABR 0x20 /* 2 DABRs */ +#define SS_DBCR 0x28 +#define SS_SP 0x2c +#define SS_SR 0x30 /* 16 segment registers */ +#define SS_R2 0x70 +#define SS_MSR 0x74 +#define SS_SDR1 0x78 +#define SS_LR 0x7c +#define SS_SPRG 0x80 /* 4 SPRGs */ +#define SS_DBAT 0x90 /* 8 DBATs */ +#define SS_IBAT 0xd0 /* 8 IBATs */ +#define SS_TB 0x110 +#define SS_CR 0x118 +#define SS_GPREG 0x11c /* r12-r31 */ +#define STATE_SAVE_SIZE 0x16c + + .section .data + .align 5 + +mpc83xx_sleep_save_area: + .space STATE_SAVE_SIZE +immrbase: + .long 0 + + .section .text + .align 5 + + /* r3 = physical address of IMMR */ +_GLOBAL(mpc83xx_enter_deep_sleep) + lis r4, immrbase@ha + stw r3, immrbase@l(r4) + + /* The first 2 words of memory are used to communicate with the + * bootloader, to tell it how to resume. + * + * The first word is the magic number 0xf5153ae5, and the second + * is the pointer to mpc83xx_deep_resume. + * + * The original content of these two words is saved in SS_MEMSAVE. + */ + + lis r3, mpc83xx_sleep_save_area@h + ori r3, r3, mpc83xx_sleep_save_area@l + + lis r4, KERNELBASE@h + lwz r5, 0(r4) + lwz r6, 4(r4) + + stw r5, SS_MEMSAVE+0(r3) + stw r6, SS_MEMSAVE+4(r3) + + mfspr r5, SPRN_HID0 + mfspr r6, SPRN_HID1 + mfspr r7, SPRN_HID2 + + stw r5, SS_HID+0(r3) + stw r6, SS_HID+4(r3) + stw r7, SS_HID+8(r3) + + mfspr r4, SPRN_IABR + mfspr r5, SPRN_IABR2 + mfspr r6, SPRN_IBCR + mfspr r7, SPRN_DABR + mfspr r8, SPRN_DABR2 + mfspr r9, SPRN_DBCR + + stw r4, SS_IABR+0(r3) + stw r5, SS_IABR+4(r3) + stw r6, SS_IBCR(r3) + stw r7, SS_DABR+0(r3) + stw r8, SS_DABR+4(r3) + stw r9, SS_DBCR(r3) + + mfspr r4, SPRN_SPRG0 + mfspr r5, SPRN_SPRG1 + mfspr r6, SPRN_SPRG2 + mfspr r7, SPRN_SPRG3 + mfsdr1 r8 + + stw r4, SS_SPRG+0(r3) + stw r5, SS_SPRG+4(r3) + stw r6, SS_SPRG+8(r3) + stw r7, SS_SPRG+12(r3) + stw r8, SS_SDR1(r3) + + mfspr r4, SPRN_DBAT0U + mfspr r5, SPRN_DBAT0L + mfspr r6, SPRN_DBAT1U + mfspr r7, SPRN_DBAT1L + + stw r4, SS_DBAT+0x00(r3) + stw r5, SS_DBAT+0x04(r3) + stw r6, SS_DBAT+0x08(r3) + stw r7, SS_DBAT+0x0c(r3) + + mfspr r4, SPRN_DBAT2U + mfspr r5, SPRN_DBAT2L + mfspr r6, SPRN_DBAT3U + mfspr r7, SPRN_DBAT3L + + stw r4, SS_DBAT+0x10(r3) + stw r5, SS_DBAT+0x14(r3) + stw r6, SS_DBAT+0x18(r3) + stw r7, SS_DBAT+0x1c(r3) + + mfspr r4, SPRN_DBAT4U + mfspr r5, SPRN_DBAT4L + mfspr r6, SPRN_DBAT5U + mfspr r7, SPRN_DBAT5L + + stw r4, SS_DBAT+0x20(r3) + stw r5, SS_DBAT+0x24(r3) + stw r6, SS_DBAT+0x28(r3) + stw r7, SS_DBAT+0x2c(r3) + + mfspr r4, SPRN_DBAT6U + mfspr r5, SPRN_DBAT6L + mfspr r6, SPRN_DBAT7U + mfspr r7, SPRN_DBAT7L + + stw r4, SS_DBAT+0x30(r3) + stw r5, SS_DBAT+0x34(r3) + stw r6, SS_DBAT+0x38(r3) + stw r7, SS_DBAT+0x3c(r3) + + mfspr r4, SPRN_IBAT0U + mfspr r5, SPRN_IBAT0L + mfspr r6, SPRN_IBAT1U + mfspr r7, SPRN_IBAT1L + + stw r4, SS_IBAT+0x00(r3) + stw r5, SS_IBAT+0x04(r3) + stw r6, SS_IBAT+0x08(r3) + stw r7, SS_IBAT+0x0c(r3) + + mfspr r4, SPRN_IBAT2U + mfspr r5, SPRN_IBAT2L + mfspr r6, SPRN_IBAT3U + mfspr r7, SPRN_IBAT3L + + stw r4, SS_IBAT+0x10(r3) + stw r5, SS_IBAT+0x14(r3) + stw r6, SS_IBAT+0x18(r3) + stw r7, SS_IBAT+0x1c(r3) + + mfspr r4, SPRN_IBAT4U + mfspr r5, SPRN_IBAT4L + mfspr r6, SPRN_IBAT5U + mfspr r7, SPRN_IBAT5L + + stw r4, SS_IBAT+0x20(r3) + stw r5, SS_IBAT+0x24(r3) + stw r6, SS_IBAT+0x28(r3) + stw r7, SS_IBAT+0x2c(r3) + + mfspr r4, SPRN_IBAT6U + mfspr r5, SPRN_IBAT6L + mfspr r6, SPRN_IBAT7U + mfspr r7, SPRN_IBAT7L + + stw r4, SS_IBAT+0x30(r3) + stw r5, SS_IBAT+0x34(r3) + stw r6, SS_IBAT+0x38(r3) + stw r7, SS_IBAT+0x3c(r3) + + mfmsr r4 + mflr r5 + mfcr r6 + + stw r4, SS_MSR(r3) + stw r5, SS_LR(r3) + stw r6, SS_CR(r3) + stw r1, SS_SP(r3) + stw r2, SS_R2(r3) + +1: mftbu r4 + mftb r5 + mftbu r6 + cmpw r4, r6 + bne 1b + + stw r4, SS_TB+0(r3) + stw r5, SS_TB+4(r3) + + stmw r12, SS_GPREG(r3) + + li r4, 0 + addi r6, r3, SS_SR-4 +1: mfsrin r5, r4 + stwu r5, 4(r6) + addis r4, r4, 0x1000 + cmpwi r4, 0 + bne 1b + + /* Disable machine checks and critical exceptions */ + mfmsr r4 + rlwinm r4, r4, 0, ~MSR_CE + rlwinm r4, r4, 0, ~MSR_ME + mtmsr r4 + isync + +#define TMP_VIRT_IMMR 0xf0000000 +#define DEFAULT_IMMR_VALUE 0xff400000 +#define IMMRBAR_BASE 0x0000 + + lis r4, immrbase@ha + lwz r4, immrbase@l(r4) + + /* Use DBAT0 to address the current IMMR space */ + + ori r4, r4, 0x002a + mtspr SPRN_DBAT0L, r4 + lis r8, TMP_VIRT_IMMR@h + ori r4, r8, 0x001e /* 1 MByte accessable from Kernel Space only */ + mtspr SPRN_DBAT0U, r4 + isync + + /* Use DBAT1 to address the original IMMR space */ + + lis r4, DEFAULT_IMMR_VALUE@h + ori r4, r4, 0x002a + mtspr SPRN_DBAT1L, r4 + lis r9, (TMP_VIRT_IMMR + 0x01000000)@h + ori r4, r9, 0x001e /* 1 MByte accessable from Kernel Space only */ + mtspr SPRN_DBAT1U, r4 + isync + + /* Use DBAT2 to address the beginning of RAM. This isn't done + * using the normal virtual mapping, because with page debugging + * enabled it will be read-only. + */ + + li r4, 0x0002 + mtspr SPRN_DBAT2L, r4 + lis r4, KERNELBASE@h + ori r4, r4, 0x001e /* 1 MByte accessable from Kernel Space only */ + mtspr SPRN_DBAT2U, r4 + isync + + /* Flush the cache with our BAT, as there will be TLB misses + * otherwise if page debugging is enabled, and these misses + * will disturb the PLRU algorithm. + */ + + bl __flush_disable_L1 + + /* Keep the i-cache enabled, so the hack below for low-boot + * flash will work. + */ + mfspr r3, SPRN_HID0 + ori r3, r3, HID0_ICE + mtspr SPRN_HID0, r3 + isync + + lis r6, 0xf515 + ori r6, r6, 0x3ae5 + + lis r7, mpc83xx_deep_resume@h + ori r7, r7, mpc83xx_deep_resume@l + tophys(r7, r7) + + lis r5, KERNELBASE@h + stw r6, 0(r5) + stw r7, 4(r5) + + /* Reset BARs */ + + li r4, 0 + stw r4, 0x0024(r8) + stw r4, 0x002c(r8) + stw r4, 0x0034(r8) + stw r4, 0x003c(r8) + stw r4, 0x0064(r8) + stw r4, 0x006c(r8) + + /* Rev 1 of the 8313 has problems with wakeup events that are + * pending during the transition to deep sleep state (such as if + * the PCI host sets the state to D3 and then D0 in rapid + * succession). This check shrinks the race window somewhat. + * + * See erratum PCI23, though the problem is not limited + * to PCI. + */ + + lwz r3, 0x0b04(r8) + andi. r3, r3, 1 + bne- mpc83xx_deep_resume + + /* Move IMMR back to the default location, following the + * procedure specified in the MPC8313 manual. + */ + lwz r4, IMMRBAR_BASE(r8) + isync + lis r4, DEFAULT_IMMR_VALUE@h + stw r4, IMMRBAR_BASE(r8) + lis r4, KERNELBASE@h + lwz r4, 0(r4) + isync + lwz r4, IMMRBAR_BASE(r9) + mr r8, r9 + isync + + /* Check the Reset Configuration Word to see whether flash needs + * to be mapped at a low address or a high address. + */ + + lwz r4, 0x0904(r8) + andis. r4, r4, 0x0400 + li r4, 0 + beq boot_low + lis r4, 0xff80 +boot_low: + stw r4, 0x0020(r8) + lis r7, 0x8000 + ori r7, r7, 0x0016 + + mfspr r5, SPRN_HID0 + rlwinm r5, r5, 0, ~(HID0_DOZE | HID0_NAP) + oris r5, r5, HID0_SLEEP@h + mtspr SPRN_HID0, r5 + isync + + mfmsr r5 + oris r5, r5, MSR_POW@h + + /* Enable the flash mapping at the appropriate address. This + * mapping will override the RAM mapping if booting low, so there's + * no need to disable the latter. This must be done inside the same + * cache line as setting MSR_POW, so that no instruction fetches + * from RAM happen after the flash mapping is turned on. + */ + + .align 5 + stw r7, 0x0024(r8) + sync + isync + mtmsr r5 + isync +1: b 1b + +mpc83xx_deep_resume: + lis r4, 1f@h + ori r4, r4, 1f@l + tophys(r4, r4) + mtsrr0 r4 + + mfmsr r4 + rlwinm r4, r4, 0, ~(MSR_IR | MSR_DR) + mtsrr1 r4 + + rfi + +1: tlbia + bl __inval_enable_L1 + + lis r3, mpc83xx_sleep_save_area@h + ori r3, r3, mpc83xx_sleep_save_area@l + tophys(r3, r3) + + lwz r5, SS_MEMSAVE+0(r3) + lwz r6, SS_MEMSAVE+4(r3) + + stw r5, 0(0) + stw r6, 4(0) + + lwz r5, SS_HID+0(r3) + lwz r6, SS_HID+4(r3) + lwz r7, SS_HID+8(r3) + + mtspr SPRN_HID0, r5 + mtspr SPRN_HID1, r6 + mtspr SPRN_HID2, r7 + + lwz r4, SS_IABR+0(r3) + lwz r5, SS_IABR+4(r3) + lwz r6, SS_IBCR(r3) + lwz r7, SS_DABR+0(r3) + lwz r8, SS_DABR+4(r3) + lwz r9, SS_DBCR(r3) + + mtspr SPRN_IABR, r4 + mtspr SPRN_IABR2, r5 + mtspr SPRN_IBCR, r6 + mtspr SPRN_DABR, r7 + mtspr SPRN_DABR2, r8 + mtspr SPRN_DBCR, r9 + + li r4, 0 + addi r6, r3, SS_SR-4 +1: lwzu r5, 4(r6) + mtsrin r5, r4 + addis r4, r4, 0x1000 + cmpwi r4, 0 + bne 1b + + lwz r4, SS_DBAT+0x00(r3) + lwz r5, SS_DBAT+0x04(r3) + lwz r6, SS_DBAT+0x08(r3) + lwz r7, SS_DBAT+0x0c(r3) + + mtspr SPRN_DBAT0U, r4 + mtspr SPRN_DBAT0L, r5 + mtspr SPRN_DBAT1U, r6 + mtspr SPRN_DBAT1L, r7 + + lwz r4, SS_DBAT+0x10(r3) + lwz r5, SS_DBAT+0x14(r3) + lwz r6, SS_DBAT+0x18(r3) + lwz r7, SS_DBAT+0x1c(r3) + + mtspr SPRN_DBAT2U, r4 + mtspr SPRN_DBAT2L, r5 + mtspr SPRN_DBAT3U, r6 + mtspr SPRN_DBAT3L, r7 + + lwz r4, SS_DBAT+0x20(r3) + lwz r5, SS_DBAT+0x24(r3) + lwz r6, SS_DBAT+0x28(r3) + lwz r7, SS_DBAT+0x2c(r3) + + mtspr SPRN_DBAT4U, r4 + mtspr SPRN_DBAT4L, r5 + mtspr SPRN_DBAT5U, r6 + mtspr SPRN_DBAT5L, r7 + + lwz r4, SS_DBAT+0x30(r3) + lwz r5, SS_DBAT+0x34(r3) + lwz r6, SS_DBAT+0x38(r3) + lwz r7, SS_DBAT+0x3c(r3) + + mtspr SPRN_DBAT6U, r4 + mtspr SPRN_DBAT6L, r5 + mtspr SPRN_DBAT7U, r6 + mtspr SPRN_DBAT7L, r7 + + lwz r4, SS_IBAT+0x00(r3) + lwz r5, SS_IBAT+0x04(r3) + lwz r6, SS_IBAT+0x08(r3) + lwz r7, SS_IBAT+0x0c(r3) + + mtspr SPRN_IBAT0U, r4 + mtspr SPRN_IBAT0L, r5 + mtspr SPRN_IBAT1U, r6 + mtspr SPRN_IBAT1L, r7 + + lwz r4, SS_IBAT+0x10(r3) + lwz r5, SS_IBAT+0x14(r3) + lwz r6, SS_IBAT+0x18(r3) + lwz r7, SS_IBAT+0x1c(r3) + + mtspr SPRN_IBAT2U, r4 + mtspr SPRN_IBAT2L, r5 + mtspr SPRN_IBAT3U, r6 + mtspr SPRN_IBAT3L, r7 + + lwz r4, SS_IBAT+0x20(r3) + lwz r5, SS_IBAT+0x24(r3) + lwz r6, SS_IBAT+0x28(r3) + lwz r7, SS_IBAT+0x2c(r3) + + mtspr SPRN_IBAT4U, r4 + mtspr SPRN_IBAT4L, r5 + mtspr SPRN_IBAT5U, r6 + mtspr SPRN_IBAT5L, r7 + + lwz r4, SS_IBAT+0x30(r3) + lwz r5, SS_IBAT+0x34(r3) + lwz r6, SS_IBAT+0x38(r3) + lwz r7, SS_IBAT+0x3c(r3) + + mtspr SPRN_IBAT6U, r4 + mtspr SPRN_IBAT6L, r5 + mtspr SPRN_IBAT7U, r6 + mtspr SPRN_IBAT7L, r7 + + lwz r4, SS_SPRG+0(r3) + lwz r5, SS_SPRG+4(r3) + lwz r6, SS_SPRG+8(r3) + lwz r7, SS_SPRG+12(r3) + lwz r8, SS_SDR1(r3) + + mtspr SPRN_SPRG0, r4 + mtspr SPRN_SPRG1, r5 + mtspr SPRN_SPRG2, r6 + mtspr SPRN_SPRG3, r7 + mtsdr1 r8 + + lwz r4, SS_MSR(r3) + lwz r5, SS_LR(r3) + lwz r6, SS_CR(r3) + lwz r1, SS_SP(r3) + lwz r2, SS_R2(r3) + + mtsrr1 r4 + mtsrr0 r5 + mtcr r6 + + li r4, 0 + mtspr SPRN_TBWL, r4 + + lwz r4, SS_TB+0(r3) + lwz r5, SS_TB+4(r3) + + mtspr SPRN_TBWU, r4 + mtspr SPRN_TBWL, r5 + + lmw r12, SS_GPREG(r3) + + /* Kick decrementer */ + li r0, 1 + mtdec r0 + + rfi diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c new file mode 100644 index 000000000000..08e65fc8b98c --- /dev/null +++ b/arch/powerpc/platforms/83xx/suspend.c @@ -0,0 +1,388 @@ +/* + * MPC83xx suspend support + * + * Author: Scott Wood + * + * Copyright (c) 2006-2007 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 version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define PMCCR1_NEXT_STATE 0x0C /* Next state for power management */ +#define PMCCR1_NEXT_STATE_SHIFT 2 +#define PMCCR1_CURR_STATE 0x03 /* Current state for power management*/ +#define IMMR_RCW_OFFSET 0x900 +#define RCW_PCI_HOST 0x80000000 + +void mpc83xx_enter_deep_sleep(phys_addr_t immrbase); + +struct mpc83xx_pmc { + u32 config; +#define PMCCR_DLPEN 2 /* DDR SDRAM low power enable */ +#define PMCCR_SLPEN 1 /* System low power enable */ + + u32 event; + u32 mask; +/* All but PMCI are deep-sleep only */ +#define PMCER_GPIO 0x100 +#define PMCER_PCI 0x080 +#define PMCER_USB 0x040 +#define PMCER_ETSEC1 0x020 +#define PMCER_ETSEC2 0x010 +#define PMCER_TIMER 0x008 +#define PMCER_INT1 0x004 +#define PMCER_INT2 0x002 +#define PMCER_PMCI 0x001 +#define PMCER_ALL 0x1FF + + /* deep-sleep only */ + u32 config1; +#define PMCCR1_USE_STATE 0x80000000 +#define PMCCR1_PME_EN 0x00000080 +#define PMCCR1_ASSERT_PME 0x00000040 +#define PMCCR1_POWER_OFF 0x00000020 + + /* deep-sleep only */ + u32 config2; +}; + +struct mpc83xx_rcw { + u32 rcwlr; + u32 rcwhr; +}; + +struct mpc83xx_clock { + u32 spmr; + u32 occr; + u32 sccr; +}; + +struct pmc_type { + int has_deep_sleep; +}; + +static struct of_device *pmc_dev; +static int has_deep_sleep, deep_sleeping; +static int pmc_irq; +static struct mpc83xx_pmc __iomem *pmc_regs; +static struct mpc83xx_clock __iomem *clock_regs; +static int is_pci_agent, wake_from_pci; +static phys_addr_t immrbase; +static int pci_pm_state; +static DECLARE_WAIT_QUEUE_HEAD(agent_wq); + +int fsl_deep_sleep(void) +{ + return deep_sleeping; +} + +static int mpc83xx_change_state(void) +{ + u32 curr_state; + u32 reg_cfg1 = in_be32(&pmc_regs->config1); + + if (is_pci_agent) { + pci_pm_state = (reg_cfg1 & PMCCR1_NEXT_STATE) >> + PMCCR1_NEXT_STATE_SHIFT; + curr_state = reg_cfg1 & PMCCR1_CURR_STATE; + + if (curr_state != pci_pm_state) { + reg_cfg1 &= ~PMCCR1_CURR_STATE; + reg_cfg1 |= pci_pm_state; + out_be32(&pmc_regs->config1, reg_cfg1); + + wake_up(&agent_wq); + return 1; + } + } + + return 0; +} + +static irqreturn_t pmc_irq_handler(int irq, void *dev_id) +{ + u32 event = in_be32(&pmc_regs->event); + int ret = IRQ_NONE; + + if (mpc83xx_change_state()) + ret = IRQ_HANDLED; + + if (event) { + out_be32(&pmc_regs->event, event); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int mpc83xx_suspend_enter(suspend_state_t state) +{ + int ret = -EAGAIN; + + /* Don't go to sleep if there's a race where pci_pm_state changes + * between the agent thread checking it and the PM code disabling + * interrupts. + */ + if (wake_from_pci) { + if (pci_pm_state != (deep_sleeping ? 3 : 2)) + goto out; + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) | PMCCR1_PME_EN); + } + + /* Put the system into low-power mode and the RAM + * into self-refresh mode once the core goes to + * sleep. + */ + + out_be32(&pmc_regs->config, PMCCR_SLPEN | PMCCR_DLPEN); + + /* If it has deep sleep (i.e. it's an 831x or compatible), + * disable power to the core upon entering sleep mode. This will + * require going through the boot firmware upon a wakeup event. + */ + + if (deep_sleeping) { + out_be32(&pmc_regs->mask, PMCER_ALL); + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) | PMCCR1_POWER_OFF); + + enable_kernel_fp(); + + mpc83xx_enter_deep_sleep(immrbase); + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) & ~PMCCR1_POWER_OFF); + + out_be32(&pmc_regs->mask, PMCER_PMCI); + } else { + out_be32(&pmc_regs->mask, PMCER_PMCI); + + mpc6xx_enter_standby(); + } + + ret = 0; + +out: + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) & ~PMCCR1_PME_EN); + + return ret; +} + +static void mpc83xx_suspend_finish(void) +{ + deep_sleeping = 0; +} + +static int mpc83xx_suspend_valid(suspend_state_t state) +{ + return state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM; +} + +static int mpc83xx_suspend_begin(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + deep_sleeping = 0; + return 0; + + case PM_SUSPEND_MEM: + if (has_deep_sleep) + deep_sleeping = 1; + + return 0; + + default: + return -EINVAL; + } +} + +static int agent_thread_fn(void *data) +{ + while (1) { + wait_event_interruptible(agent_wq, pci_pm_state >= 2); + try_to_freeze(); + + if (signal_pending(current) || pci_pm_state < 2) + continue; + + /* With a preemptible kernel (or SMP), this could race with + * a userspace-driven suspend request. It's probably best + * to avoid mixing the two with such a configuration (or + * else fix it by adding a mutex to state_store that we can + * synchronize with). + */ + + wake_from_pci = 1; + + pm_suspend(pci_pm_state == 3 ? PM_SUSPEND_MEM : + PM_SUSPEND_STANDBY); + + wake_from_pci = 0; + } + + return 0; +} + +static void mpc83xx_set_agent(void) +{ + out_be32(&pmc_regs->config1, PMCCR1_USE_STATE); + out_be32(&pmc_regs->mask, PMCER_PMCI); + + kthread_run(agent_thread_fn, NULL, "PCI power mgt"); +} + +static int mpc83xx_is_pci_agent(void) +{ + struct mpc83xx_rcw __iomem *rcw_regs; + int ret; + + rcw_regs = ioremap(get_immrbase() + IMMR_RCW_OFFSET, + sizeof(struct mpc83xx_rcw)); + + if (!rcw_regs) + return -ENOMEM; + + ret = !(in_be32(&rcw_regs->rcwhr) & RCW_PCI_HOST); + + iounmap(rcw_regs); + return ret; +} + +static struct platform_suspend_ops mpc83xx_suspend_ops = { + .valid = mpc83xx_suspend_valid, + .begin = mpc83xx_suspend_begin, + .enter = mpc83xx_suspend_enter, + .finish = mpc83xx_suspend_finish, +}; + +static int pmc_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct device_node *np = ofdev->node; + struct resource res; + struct pmc_type *type = match->data; + int ret = 0; + + if (!of_device_is_available(np)) + return -ENODEV; + + has_deep_sleep = type->has_deep_sleep; + immrbase = get_immrbase(); + pmc_dev = ofdev; + + is_pci_agent = mpc83xx_is_pci_agent(); + if (is_pci_agent < 0) + return is_pci_agent; + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return -ENODEV; + + pmc_irq = irq_of_parse_and_map(np, 0); + if (pmc_irq != NO_IRQ) { + ret = request_irq(pmc_irq, pmc_irq_handler, IRQF_SHARED, + "pmc", ofdev); + + if (ret) + return -EBUSY; + } + + pmc_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); + + if (!pmc_regs) { + ret = -ENOMEM; + goto out; + } + + ret = of_address_to_resource(np, 1, &res); + if (ret) { + ret = -ENODEV; + goto out_pmc; + } + + clock_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); + + if (!clock_regs) { + ret = -ENOMEM; + goto out_pmc; + } + + if (is_pci_agent) + mpc83xx_set_agent(); + + suspend_set_ops(&mpc83xx_suspend_ops); + return 0; + +out_pmc: + iounmap(pmc_regs); +out: + if (pmc_irq != NO_IRQ) + free_irq(pmc_irq, ofdev); + + return ret; +} + +static int pmc_remove(struct of_device *ofdev) +{ + return -EPERM; +}; + +static struct pmc_type pmc_types[] = { + { + .has_deep_sleep = 1, + }, + { + .has_deep_sleep = 0, + } +}; + +static struct of_device_id pmc_match[] = { + { + .compatible = "fsl,mpc8313-pmc", + .data = &pmc_types[0], + }, + { + .compatible = "fsl,mpc8349-pmc", + .data = &pmc_types[1], + }, + {} +}; + +static struct of_platform_driver pmc_driver = { + .name = "mpc83xx-pmc", + .match_table = pmc_match, + .probe = pmc_probe, + .remove = pmc_remove +}; + +static int pmc_init(void) +{ + return of_register_platform_driver(&pmc_driver); +} + +module_init(pmc_init); diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.h index 52c831fa1886..024299887352 100644 --- a/arch/powerpc/sysdev/fsl_soc.h +++ b/arch/powerpc/sysdev/fsl_soc.h @@ -10,6 +10,7 @@ extern u32 get_baudrate(void); extern u32 fsl_get_sys_freq(void); struct spi_board_info; +struct device_node; extern int fsl_spi_init(struct spi_board_info *board_infos, unsigned int num_board_infos, diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c index caba1c0be5a7..88a983ece5c9 100644 --- a/arch/powerpc/sysdev/ipic.c +++ b/arch/powerpc/sysdev/ipic.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -889,8 +890,78 @@ unsigned int ipic_get_irq(void) return irq_linear_revmap(primary_ipic->irqhost, irq); } +#ifdef CONFIG_PM +static struct { + u32 sicfr; + u32 siprr[2]; + u32 simsr[2]; + u32 sicnr; + u32 smprr[2]; + u32 semsr; + u32 secnr; + u32 sermr; + u32 sercr; +} ipic_saved_state; + +static int ipic_suspend(struct sys_device *sdev, pm_message_t state) +{ + struct ipic *ipic = primary_ipic; + + ipic_saved_state.sicfr = ipic_read(ipic->regs, IPIC_SICFR); + ipic_saved_state.siprr[0] = ipic_read(ipic->regs, IPIC_SIPRR_A); + ipic_saved_state.siprr[1] = ipic_read(ipic->regs, IPIC_SIPRR_D); + ipic_saved_state.simsr[0] = ipic_read(ipic->regs, IPIC_SIMSR_H); + ipic_saved_state.simsr[1] = ipic_read(ipic->regs, IPIC_SIMSR_L); + ipic_saved_state.sicnr = ipic_read(ipic->regs, IPIC_SICNR); + ipic_saved_state.smprr[0] = ipic_read(ipic->regs, IPIC_SMPRR_A); + ipic_saved_state.smprr[1] = ipic_read(ipic->regs, IPIC_SMPRR_B); + ipic_saved_state.semsr = ipic_read(ipic->regs, IPIC_SEMSR); + ipic_saved_state.secnr = ipic_read(ipic->regs, IPIC_SECNR); + ipic_saved_state.sermr = ipic_read(ipic->regs, IPIC_SERMR); + ipic_saved_state.sercr = ipic_read(ipic->regs, IPIC_SERCR); + + if (fsl_deep_sleep()) { + /* In deep sleep, make sure there can be no + * pending interrupts, as this can cause + * problems on 831x. + */ + ipic_write(ipic->regs, IPIC_SIMSR_H, 0); + ipic_write(ipic->regs, IPIC_SIMSR_L, 0); + ipic_write(ipic->regs, IPIC_SEMSR, 0); + ipic_write(ipic->regs, IPIC_SERMR, 0); + } + + return 0; +} + +static int ipic_resume(struct sys_device *sdev) +{ + struct ipic *ipic = primary_ipic; + + ipic_write(ipic->regs, IPIC_SICFR, ipic_saved_state.sicfr); + ipic_write(ipic->regs, IPIC_SIPRR_A, ipic_saved_state.siprr[0]); + ipic_write(ipic->regs, IPIC_SIPRR_D, ipic_saved_state.siprr[1]); + ipic_write(ipic->regs, IPIC_SIMSR_H, ipic_saved_state.simsr[0]); + ipic_write(ipic->regs, IPIC_SIMSR_L, ipic_saved_state.simsr[1]); + ipic_write(ipic->regs, IPIC_SICNR, ipic_saved_state.sicnr); + ipic_write(ipic->regs, IPIC_SMPRR_A, ipic_saved_state.smprr[0]); + ipic_write(ipic->regs, IPIC_SMPRR_B, ipic_saved_state.smprr[1]); + ipic_write(ipic->regs, IPIC_SEMSR, ipic_saved_state.semsr); + ipic_write(ipic->regs, IPIC_SECNR, ipic_saved_state.secnr); + ipic_write(ipic->regs, IPIC_SERMR, ipic_saved_state.sermr); + ipic_write(ipic->regs, IPIC_SERCR, ipic_saved_state.sercr); + + return 0; +} +#else +#define ipic_suspend NULL +#define ipic_resume NULL +#endif + static struct sysdev_class ipic_sysclass = { .name = "ipic", + .suspend = ipic_suspend, + .resume = ipic_resume, }; static struct sys_device device_ipic = { diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h index bbccadfee0d6..c6d1ab650778 100644 --- a/include/asm-powerpc/reg.h +++ b/include/asm-powerpc/reg.h @@ -155,10 +155,12 @@ #define CTRL_RUNLATCH 0x1 #define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */ #define DABR_TRANSLATION (1UL << 2) +#define SPRN_DABR2 0x13D /* e300 */ #define SPRN_DABRX 0x3F7 /* Data Address Breakpoint Register Extension */ #define DABRX_USER (1UL << 0) #define DABRX_KERNEL (1UL << 1) #define SPRN_DAR 0x013 /* Data Address Register */ +#define SPRN_DBCR 0x136 /* e300 Data Breakpoint Control Reg */ #define SPRN_DSISR 0x012 /* Data Storage Interrupt Status Register */ #define DSISR_NOHPTE 0x40000000 /* no translation found */ #define DSISR_PROTFAULT 0x08000000 /* protection fault */ @@ -264,6 +266,8 @@ #define HID1_PS (1<<16) /* 750FX PLL selection */ #define SPRN_HID2 0x3F8 /* Hardware Implementation Register 2 */ #define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */ +#define SPRN_IABR2 0x3FA /* 83xx */ +#define SPRN_IBCR 0x135 /* 83xx Insn Breakpoint Control Reg */ #define SPRN_HID4 0x3F4 /* 970 HID4 */ #define SPRN_HID5 0x3F6 /* 970 HID5 */ #define SPRN_HID6 0x3F9 /* BE HID 6 */ diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index c415a496de3a..0472877d7ea8 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -125,4 +125,10 @@ struct mpc8xx_pcmcia_ops { int(*voltage_set)(int slot, int vcc, int vpp); }; +/* Returns non-zero if the current suspend operation would + * lead to a deep sleep (i.e. power removed from the core, + * instead of just the clock). + */ +int fsl_deep_sleep(void); + #endif /* _FSL_DEVICE_H_ */ -- cgit From d87eb12785c14de1586e3bad86ca2c0991300339 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 11 Jul 2008 18:04:45 -0500 Subject: gianfar: Add magic packet and suspend/resume support. Signed-off-by: Scott Wood Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_soc.c | 3 ++ drivers/net/gianfar.c | 122 +++++++++++++++++++++++++++++++++++++++++- drivers/net/gianfar.h | 12 ++++- drivers/net/gianfar_ethtool.c | 41 +++++++++++++- include/linux/fsl_devices.h | 1 + 5 files changed, 173 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index ef4cb0d67a72..214388e11807 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -352,6 +352,9 @@ static int __init gfar_of_init(void) else gfar_data.interface = PHY_INTERFACE_MODE_MII; + if (of_get_property(np, "fsl,magic-packet", NULL)) + gfar_data.device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET; + ph = of_get_property(np, "phy-handle", NULL); if (ph == NULL) { u32 *fixed_link; diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 25bdd0832df5..36f229ff52f2 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -143,6 +143,9 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int l static void gfar_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp); void gfar_halt(struct net_device *dev); +#ifdef CONFIG_PM +static void gfar_halt_nodisable(struct net_device *dev); +#endif void gfar_start(struct net_device *dev); static void gfar_clear_exact_match(struct net_device *dev); static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr); @@ -216,6 +219,7 @@ static int gfar_probe(struct platform_device *pdev) spin_lock_init(&priv->txlock); spin_lock_init(&priv->rxlock); + spin_lock_init(&priv->bflock); platform_set_drvdata(pdev, dev); @@ -393,6 +397,103 @@ static int gfar_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int gfar_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + u32 tempval; + + int magic_packet = priv->wol_en && + (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + + netif_device_detach(dev); + + if (netif_running(dev)) { + spin_lock_irqsave(&priv->txlock, flags); + spin_lock(&priv->rxlock); + + gfar_halt_nodisable(dev); + + /* Disable Tx, and Rx if wake-on-LAN is disabled. */ + tempval = gfar_read(&priv->regs->maccfg1); + + tempval &= ~MACCFG1_TX_EN; + + if (!magic_packet) + tempval &= ~MACCFG1_RX_EN; + + gfar_write(&priv->regs->maccfg1, tempval); + + spin_unlock(&priv->rxlock); + spin_unlock_irqrestore(&priv->txlock, flags); + +#ifdef CONFIG_GFAR_NAPI + napi_disable(&priv->napi); +#endif + + if (magic_packet) { + /* Enable interrupt on Magic Packet */ + gfar_write(&priv->regs->imask, IMASK_MAG); + + /* Enable Magic Packet mode */ + tempval = gfar_read(&priv->regs->maccfg2); + tempval |= MACCFG2_MPEN; + gfar_write(&priv->regs->maccfg2, tempval); + } else { + phy_stop(priv->phydev); + } + } + + return 0; +} + +static int gfar_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + u32 tempval; + int magic_packet = priv->wol_en && + (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + + if (!netif_running(dev)) { + netif_device_attach(dev); + return 0; + } + + if (!magic_packet && priv->phydev) + phy_start(priv->phydev); + + /* Disable Magic Packet mode, in case something + * else woke us up. + */ + + spin_lock_irqsave(&priv->txlock, flags); + spin_lock(&priv->rxlock); + + tempval = gfar_read(&priv->regs->maccfg2); + tempval &= ~MACCFG2_MPEN; + gfar_write(&priv->regs->maccfg2, tempval); + + gfar_start(dev); + + spin_unlock(&priv->rxlock); + spin_unlock_irqrestore(&priv->txlock, flags); + + netif_device_attach(dev); + +#ifdef CONFIG_GFAR_NAPI + napi_enable(&priv->napi); +#endif + + return 0; +} +#else +#define gfar_suspend NULL +#define gfar_resume NULL +#endif /* Reads the controller's registers to determine what interface * connects it to the PHY. @@ -549,8 +650,9 @@ static void init_registers(struct net_device *dev) } +#ifdef CONFIG_PM /* Halt the receive and transmit queues */ -void gfar_halt(struct net_device *dev) +static void gfar_halt_nodisable(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); struct gfar __iomem *regs = priv->regs; @@ -573,6 +675,15 @@ void gfar_halt(struct net_device *dev) (IEVENT_GRSC | IEVENT_GTSC))) cpu_relax(); } +} +#endif + +/* Halt the receive and transmit queues */ +void gfar_halt(struct net_device *dev) +{ + struct gfar_private *priv = netdev_priv(dev); + struct gfar __iomem *regs = priv->regs; + u32 tempval; /* Disable Rx and Tx */ tempval = gfar_read(®s->maccfg1); @@ -1969,7 +2080,12 @@ static irqreturn_t gfar_error(int irq, void *dev_id) u32 events = gfar_read(&priv->regs->ievent); /* Clear IEVENT */ - gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK); + gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK); + + /* Magic Packet is not an error. */ + if ((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && + (events & IEVENT_MAG)) + events &= ~IEVENT_MAG; /* Hmm... */ if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) @@ -2042,6 +2158,8 @@ MODULE_ALIAS("platform:fsl-gianfar"); static struct platform_driver gfar_driver = { .probe = gfar_probe, .remove = gfar_remove, + .suspend = gfar_suspend, + .resume = gfar_resume, .driver = { .name = "fsl-gianfar", .owner = THIS_MODULE, diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 27f37c81e52c..5ee518a8782b 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -168,6 +168,7 @@ extern const char gfar_driver_version[]; #define MACCFG2_GMII 0x00000200 #define MACCFG2_HUGEFRAME 0x00000020 #define MACCFG2_LENGTHCHECK 0x00000010 +#define MACCFG2_MPEN 0x00000008 #define ECNTRL_INIT_SETTINGS 0x00001000 #define ECNTRL_TBI_MODE 0x00000020 @@ -240,6 +241,7 @@ extern const char gfar_driver_version[]; #define IEVENT_CRL 0x00020000 #define IEVENT_XFUN 0x00010000 #define IEVENT_RXB0 0x00008000 +#define IEVENT_MAG 0x00000800 #define IEVENT_GRSC 0x00000100 #define IEVENT_RXF0 0x00000080 #define IEVENT_FIR 0x00000008 @@ -252,7 +254,8 @@ extern const char gfar_driver_version[]; #define IEVENT_ERR_MASK \ (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \ IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \ - | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR) + | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR \ + | IEVENT_MAG) #define IMASK_INIT_CLEAR 0x00000000 #define IMASK_BABR 0x80000000 @@ -270,6 +273,7 @@ extern const char gfar_driver_version[]; #define IMASK_CRL 0x00020000 #define IMASK_XFUN 0x00010000 #define IMASK_RXB0 0x00008000 +#define IMASK_MAG 0x00000800 #define IMASK_GTSC 0x00000100 #define IMASK_RXFEN0 0x00000080 #define IMASK_FIR 0x00000008 @@ -737,10 +741,14 @@ struct gfar_private { unsigned int fifo_starve; unsigned int fifo_starve_off; + /* Bitfield update lock */ + spinlock_t bflock; + unsigned char vlan_enable:1, rx_csum_enable:1, extended_hash:1, - bd_stash_en:1; + bd_stash_en:1, + wol_en:1; /* Wake-on-LAN enabled */ unsigned short padding; unsigned int interruptTransmit; diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 6007147cc1e9..fb7d3ccc0fdc 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -479,14 +479,13 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) { struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; int err = 0; if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) return -EOPNOTSUPP; if (dev->flags & IFF_UP) { - unsigned long flags; - /* Halt TX and RX, and process the frames which * have already been received */ spin_lock_irqsave(&priv->txlock, flags); @@ -502,7 +501,9 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) stop_gfar(dev); } + spin_lock_irqsave(&priv->bflock, flags); priv->rx_csum_enable = data; + spin_unlock_irqrestore(&priv->bflock, flags); if (dev->flags & IFF_UP) err = startup_gfar(dev); @@ -564,6 +565,38 @@ static void gfar_set_msglevel(struct net_device *dev, uint32_t data) priv->msg_enable = data; } +#ifdef CONFIG_PM +static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct gfar_private *priv = netdev_priv(dev); + + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) { + wol->supported = WAKE_MAGIC; + wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0; + } else { + wol->supported = wol->wolopts = 0; + } +} + +static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct gfar_private *priv = netdev_priv(dev); + unsigned long flags; + + if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && + wol->wolopts != 0) + return -EINVAL; + + if (wol->wolopts & ~WAKE_MAGIC) + return -EINVAL; + + spin_lock_irqsave(&priv->bflock, flags); + priv->wol_en = wol->wolopts & WAKE_MAGIC ? 1 : 0; + spin_unlock_irqrestore(&priv->bflock, flags); + + return 0; +} +#endif const struct ethtool_ops gfar_ethtool_ops = { .get_settings = gfar_gsettings, @@ -585,4 +618,8 @@ const struct ethtool_ops gfar_ethtool_ops = { .set_tx_csum = gfar_set_tx_csum, .get_msglevel = gfar_get_msglevel, .set_msglevel = gfar_set_msglevel, +#ifdef CONFIG_PM + .get_wol = gfar_get_wol, + .set_wol = gfar_set_wol, +#endif }; diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 0472877d7ea8..4e625e0094c8 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -69,6 +69,7 @@ struct gianfar_mdio_data { #define FSL_GIANFAR_DEV_HAS_VLAN 0x00000020 #define FSL_GIANFAR_DEV_HAS_EXTENDED_HASH 0x00000040 #define FSL_GIANFAR_DEV_HAS_PADDING 0x00000080 +#define FSL_GIANFAR_DEV_HAS_MAGIC_PACKET 0x00000100 /* Flags in gianfar_platform_data */ #define FSL_GIANFAR_BRD_HAS_PHY_INTR 0x00000001 /* set or use a timer */ -- cgit From b219108cbacee5f2eaeca63cba013688eeba3bd4 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Thu, 12 Jun 2008 08:32:13 -0500 Subject: fs_enet: Remove !CONFIG_PPC_CPM_NEW_BINDING code Now that arch/ppc is gone we always define CONFIG_PPC_CPM_NEW_BINDING so we can remove all the code associated with !CONFIG_PPC_CPM_NEW_BINDING. Also fixed some asm/of_platform.h to linux/of_platform.h (and of_device.h) Signed-off-by: Kumar Gala --- drivers/net/fs_enet/Makefile | 5 - drivers/net/fs_enet/fs_enet-main.c | 310 +------------------------------------ drivers/net/fs_enet/fs_enet.h | 4 - drivers/net/fs_enet/mac-fcc.c | 67 +------- drivers/net/fs_enet/mac-fec.c | 23 +-- drivers/net/fs_enet/mac-scc.c | 37 +---- drivers/net/fs_enet/mii-bitbang.c | 107 ------------- drivers/net/fs_enet/mii-fec.c | 144 +---------------- include/linux/fs_enet_pd.h | 4 - 9 files changed, 5 insertions(+), 696 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/fs_enet/Makefile b/drivers/net/fs_enet/Makefile index 1ffbe0756a0c..d4a305ee3455 100644 --- a/drivers/net/fs_enet/Makefile +++ b/drivers/net/fs_enet/Makefile @@ -8,12 +8,7 @@ fs_enet-$(CONFIG_FS_ENET_HAS_SCC) += mac-scc.o fs_enet-$(CONFIG_FS_ENET_HAS_FEC) += mac-fec.o fs_enet-$(CONFIG_FS_ENET_HAS_FCC) += mac-fcc.o -ifeq ($(CONFIG_PPC_CPM_NEW_BINDING),y) obj-$(CONFIG_FS_ENET_MDIO_FEC) += mii-fec.o obj-$(CONFIG_FS_ENET_MDIO_FCC) += mii-bitbang.o -else -fs_enet-$(CONFIG_FS_ENET_MDIO_FEC) += mii-fec.o -fs_enet-$(CONFIG_FS_ENET_MDIO_FCC) += mii-bitbang.o -endif fs_enet-objs := fs_enet-main.o $(fs_enet-m) diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index 352574a3f056..ac2c48741d04 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -36,25 +36,17 @@ #include #include #include +#include #include #include #include #include -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include -#endif - #include "fs_enet.h" /*************************************************/ -#ifndef CONFIG_PPC_CPM_NEW_BINDING -static char version[] __devinitdata = - DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")" "\n"; -#endif - MODULE_AUTHOR("Pantelis Antoniou "); MODULE_DESCRIPTION("Freescale Ethernet Driver"); MODULE_LICENSE("GPL"); @@ -957,190 +949,6 @@ static int fs_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) extern int fs_mii_connect(struct net_device *dev); extern void fs_mii_disconnect(struct net_device *dev); -#ifndef CONFIG_PPC_CPM_NEW_BINDING -static struct net_device *fs_init_instance(struct device *dev, - struct fs_platform_info *fpi) -{ - struct net_device *ndev = NULL; - struct fs_enet_private *fep = NULL; - int privsize, i, r, err = 0, registered = 0; - - fpi->fs_no = fs_get_id(fpi); - /* guard */ - if ((unsigned int)fpi->fs_no >= FS_MAX_INDEX) - return ERR_PTR(-EINVAL); - - privsize = sizeof(*fep) + (sizeof(struct sk_buff **) * - (fpi->rx_ring + fpi->tx_ring)); - - ndev = alloc_etherdev(privsize); - if (!ndev) { - err = -ENOMEM; - goto err; - } - - fep = netdev_priv(ndev); - - fep->dev = dev; - dev_set_drvdata(dev, ndev); - fep->fpi = fpi; - if (fpi->init_ioports) - fpi->init_ioports((struct fs_platform_info *)fpi); - -#ifdef CONFIG_FS_ENET_HAS_FEC - if (fs_get_fec_index(fpi->fs_no) >= 0) - fep->ops = &fs_fec_ops; -#endif - -#ifdef CONFIG_FS_ENET_HAS_SCC - if (fs_get_scc_index(fpi->fs_no) >=0) - fep->ops = &fs_scc_ops; -#endif - -#ifdef CONFIG_FS_ENET_HAS_FCC - if (fs_get_fcc_index(fpi->fs_no) >= 0) - fep->ops = &fs_fcc_ops; -#endif - - if (fep->ops == NULL) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s No matching ops found (%d).\n", - ndev->name, fpi->fs_no); - err = -EINVAL; - goto err; - } - - r = (*fep->ops->setup_data)(ndev); - if (r != 0) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s setup_data failed\n", - ndev->name); - err = r; - goto err; - } - - /* point rx_skbuff, tx_skbuff */ - fep->rx_skbuff = (struct sk_buff **)&fep[1]; - fep->tx_skbuff = fep->rx_skbuff + fpi->rx_ring; - - /* init locks */ - spin_lock_init(&fep->lock); - spin_lock_init(&fep->tx_lock); - - /* - * Set the Ethernet address. - */ - for (i = 0; i < 6; i++) - ndev->dev_addr[i] = fpi->macaddr[i]; - - r = (*fep->ops->allocate_bd)(ndev); - - if (fep->ring_base == NULL) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s buffer descriptor alloc failed (%d).\n", ndev->name, r); - err = r; - goto err; - } - - /* - * Set receive and transmit descriptor base. - */ - fep->rx_bd_base = fep->ring_base; - fep->tx_bd_base = fep->rx_bd_base + fpi->rx_ring; - - /* initialize ring size variables */ - fep->tx_ring = fpi->tx_ring; - fep->rx_ring = fpi->rx_ring; - - /* - * The FEC Ethernet specific entries in the device structure. - */ - ndev->open = fs_enet_open; - ndev->hard_start_xmit = fs_enet_start_xmit; - ndev->tx_timeout = fs_timeout; - ndev->watchdog_timeo = 2 * HZ; - ndev->stop = fs_enet_close; - ndev->get_stats = fs_enet_get_stats; - ndev->set_multicast_list = fs_set_multicast_list; - -#ifdef CONFIG_NET_POLL_CONTROLLER - ndev->poll_controller = fs_enet_netpoll; -#endif - - netif_napi_add(ndev, &fep->napi, - fs_enet_rx_napi, fpi->napi_weight); - - ndev->ethtool_ops = &fs_ethtool_ops; - ndev->do_ioctl = fs_ioctl; - - init_timer(&fep->phy_timer_list); - - netif_carrier_off(ndev); - - err = register_netdev(ndev); - if (err != 0) { - printk(KERN_ERR DRV_MODULE_NAME - ": %s register_netdev failed.\n", ndev->name); - goto err; - } - registered = 1; - - - return ndev; - -err: - if (ndev != NULL) { - if (registered) - unregister_netdev(ndev); - - if (fep && fep->ops) { - (*fep->ops->free_bd)(ndev); - (*fep->ops->cleanup_data)(ndev); - } - - free_netdev(ndev); - } - - dev_set_drvdata(dev, NULL); - - return ERR_PTR(err); -} - -static int fs_cleanup_instance(struct net_device *ndev) -{ - struct fs_enet_private *fep; - const struct fs_platform_info *fpi; - struct device *dev; - - if (ndev == NULL) - return -EINVAL; - - fep = netdev_priv(ndev); - if (fep == NULL) - return -EINVAL; - - fpi = fep->fpi; - - unregister_netdev(ndev); - - dma_free_coherent(fep->dev, (fpi->tx_ring + fpi->rx_ring) * sizeof(cbd_t), - (void __force *)fep->ring_base, fep->ring_mem_addr); - - /* reset it */ - (*fep->ops->cleanup_data)(ndev); - - dev = fep->dev; - if (dev != NULL) { - dev_set_drvdata(dev, NULL); - fep->dev = NULL; - } - - free_netdev(ndev); - - return 0; -} -#endif - /**************************************************************************************/ /* handy pointer to the immap */ @@ -1167,7 +975,6 @@ static void cleanup_immap(void) /**************************************************************************************/ -#ifdef CONFIG_PPC_CPM_NEW_BINDING static int __devinit find_phy(struct device_node *np, struct fs_platform_info *fpi) { @@ -1399,121 +1206,6 @@ static void __exit fs_cleanup(void) of_unregister_platform_driver(&fs_enet_driver); cleanup_immap(); } -#else -static int __devinit fs_enet_probe(struct device *dev) -{ - struct net_device *ndev; - - /* no fixup - no device */ - if (dev->platform_data == NULL) { - printk(KERN_INFO "fs_enet: " - "probe called with no platform data; " - "remove unused devices\n"); - return -ENODEV; - } - - ndev = fs_init_instance(dev, dev->platform_data); - if (IS_ERR(ndev)) - return PTR_ERR(ndev); - return 0; -} - -static int fs_enet_remove(struct device *dev) -{ - return fs_cleanup_instance(dev_get_drvdata(dev)); -} - -static struct device_driver fs_enet_fec_driver = { - .name = "fsl-cpm-fec", - .bus = &platform_bus_type, - .probe = fs_enet_probe, - .remove = fs_enet_remove, -#ifdef CONFIG_PM -/* .suspend = fs_enet_suspend, TODO */ -/* .resume = fs_enet_resume, TODO */ -#endif -}; - -static struct device_driver fs_enet_scc_driver = { - .name = "fsl-cpm-scc", - .bus = &platform_bus_type, - .probe = fs_enet_probe, - .remove = fs_enet_remove, -#ifdef CONFIG_PM -/* .suspend = fs_enet_suspend, TODO */ -/* .resume = fs_enet_resume, TODO */ -#endif -}; - -static struct device_driver fs_enet_fcc_driver = { - .name = "fsl-cpm-fcc", - .bus = &platform_bus_type, - .probe = fs_enet_probe, - .remove = fs_enet_remove, -#ifdef CONFIG_PM -/* .suspend = fs_enet_suspend, TODO */ -/* .resume = fs_enet_resume, TODO */ -#endif -}; - -static int __init fs_init(void) -{ - int r; - - printk(KERN_INFO - "%s", version); - - r = setup_immap(); - if (r != 0) - return r; - -#ifdef CONFIG_FS_ENET_HAS_FCC - /* let's insert mii stuff */ - r = fs_enet_mdio_bb_init(); - - if (r != 0) { - printk(KERN_ERR DRV_MODULE_NAME - "BB PHY init failed.\n"); - return r; - } - r = driver_register(&fs_enet_fcc_driver); - if (r != 0) - goto err; -#endif - -#ifdef CONFIG_FS_ENET_HAS_FEC - r = fs_enet_mdio_fec_init(); - if (r != 0) { - printk(KERN_ERR DRV_MODULE_NAME - "FEC PHY init failed.\n"); - return r; - } - - r = driver_register(&fs_enet_fec_driver); - if (r != 0) - goto err; -#endif - -#ifdef CONFIG_FS_ENET_HAS_SCC - r = driver_register(&fs_enet_scc_driver); - if (r != 0) - goto err; -#endif - - return 0; -err: - cleanup_immap(); - return r; -} - -static void __exit fs_cleanup(void) -{ - driver_unregister(&fs_enet_fec_driver); - driver_unregister(&fs_enet_fcc_driver); - driver_unregister(&fs_enet_scc_driver); - cleanup_immap(); -} -#endif #ifdef CONFIG_NET_POLL_CONTROLLER static void fs_enet_netpoll(struct net_device *dev) diff --git a/drivers/net/fs_enet/fs_enet.h b/drivers/net/fs_enet/fs_enet.h index e05389c49bbb..db46d2e72329 100644 --- a/drivers/net/fs_enet/fs_enet.h +++ b/drivers/net/fs_enet/fs_enet.h @@ -138,10 +138,6 @@ struct fs_enet_private { }; /***************************************************************************/ -#ifndef CONFIG_PPC_CPM_NEW_BINDING -int fs_enet_mdio_bb_init(void); -int fs_enet_mdio_fec_init(void); -#endif void fs_init_bds(struct net_device *dev); void fs_cleanup_bds(struct net_device *dev); diff --git a/drivers/net/fs_enet/mac-fcc.c b/drivers/net/fs_enet/mac-fcc.c index 8268b3535b30..0a97fc2d97ec 100644 --- a/drivers/net/fs_enet/mac-fcc.c +++ b/drivers/net/fs_enet/mac-fcc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -42,10 +43,6 @@ #include #include -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include -#endif - #include "fs_enet.h" /*************************************************/ @@ -87,7 +84,6 @@ static inline int fcc_cr_cmd(struct fs_enet_private *fep, u32 op) static int do_pd_setup(struct fs_enet_private *fep) { -#ifdef CONFIG_PPC_CPM_NEW_BINDING struct of_device *ofdev = to_of_device(fep->dev); struct fs_platform_info *fpi = fep->fpi; int ret = -EINVAL; @@ -125,44 +121,6 @@ out_fccp: iounmap(fep->fcc.fccp); out: return ret; -#else - struct platform_device *pdev = to_platform_device(fep->dev); - struct resource *r; - - /* Fill out IRQ field */ - fep->interrupt = platform_get_irq(pdev, 0); - if (fep->interrupt < 0) - return -EINVAL; - - /* Attach the memory for the FCC Parameter RAM */ - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fcc_pram"); - fep->fcc.ep = ioremap(r->start, r->end - r->start + 1); - if (fep->fcc.ep == NULL) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fcc_regs"); - fep->fcc.fccp = ioremap(r->start, r->end - r->start + 1); - if (fep->fcc.fccp == NULL) - return -EINVAL; - - if (fep->fpi->fcc_regs_c) { - fep->fcc.fcccp = (void __iomem *)fep->fpi->fcc_regs_c; - } else { - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "fcc_regs_c"); - fep->fcc.fcccp = ioremap(r->start, - r->end - r->start + 1); - } - - if (fep->fcc.fcccp == NULL) - return -EINVAL; - - fep->fcc.mem = (void __iomem *)fep->fpi->mem_offset; - if (fep->fcc.mem == NULL) - return -EINVAL; - - return 0; -#endif } #define FCC_NAPI_RX_EVENT_MSK (FCC_ENET_RXF | FCC_ENET_RXB) @@ -173,17 +131,6 @@ out: static int setup_data(struct net_device *dev) { struct fs_enet_private *fep = netdev_priv(dev); -#ifndef CONFIG_PPC_CPM_NEW_BINDING - struct fs_platform_info *fpi = fep->fpi; - - fpi->cp_command = (fpi->cp_page << 26) | - (fpi->cp_block << 21) | - (12 << 6); - - fep->fcc.idx = fs_get_fcc_index(fpi->fs_no); - if ((unsigned int)fep->fcc.idx >= 3) /* max 3 FCCs */ - return -EINVAL; -#endif if (do_pd_setup(fep) != 0) return -EINVAL; @@ -304,9 +251,6 @@ static void restart(struct net_device *dev) fcc_enet_t __iomem *ep = fep->fcc.ep; dma_addr_t rx_bd_base_phys, tx_bd_base_phys; u16 paddrh, paddrm, paddrl; -#ifndef CONFIG_PPC_CPM_NEW_BINDING - u16 mem_addr; -#endif const unsigned char *mac; int i; @@ -338,19 +282,10 @@ static void restart(struct net_device *dev) * this area. */ -#ifdef CONFIG_PPC_CPM_NEW_BINDING W16(ep, fen_genfcc.fcc_riptr, fpi->dpram_offset); W16(ep, fen_genfcc.fcc_tiptr, fpi->dpram_offset + 32); W16(ep, fen_padptr, fpi->dpram_offset + 64); -#else - mem_addr = (u32) fep->fcc.mem; /* de-fixup dpram offset */ - - W16(ep, fen_genfcc.fcc_riptr, (mem_addr & 0xffff)); - W16(ep, fen_genfcc.fcc_tiptr, ((mem_addr + 32) & 0xffff)); - - W16(ep, fen_padptr, mem_addr + 64); -#endif /* fill with special symbol... */ memset_io(fep->fcc.mem + fpi->dpram_offset + 64, 0x88, 32); diff --git a/drivers/net/fs_enet/mac-fec.c b/drivers/net/fs_enet/mac-fec.c index 8a311d1e435b..0a7d1c5c6524 100644 --- a/drivers/net/fs_enet/mac-fec.c +++ b/drivers/net/fs_enet/mac-fec.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -43,10 +44,6 @@ #include #endif -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include -#endif - #include "fs_enet.h" #include "fec.h" @@ -99,7 +96,6 @@ static int whack_reset(fec_t __iomem *fecp) static int do_pd_setup(struct fs_enet_private *fep) { -#ifdef CONFIG_PPC_CPM_NEW_BINDING struct of_device *ofdev = to_of_device(fep->dev); fep->interrupt = of_irq_to_resource(ofdev->node, 0, NULL); @@ -111,23 +107,6 @@ static int do_pd_setup(struct fs_enet_private *fep) return -EINVAL; return 0; -#else - struct platform_device *pdev = to_platform_device(fep->dev); - struct resource *r; - - /* Fill out IRQ field */ - fep->interrupt = platform_get_irq_byname(pdev,"interrupt"); - if (fep->interrupt < 0) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - fep->fec.fecp = ioremap(r->start, r->end - r->start + 1); - - if(fep->fec.fecp == NULL) - return -EINVAL; - - return 0; -#endif } #define FEC_NAPI_RX_EVENT_MSK (FEC_ENET_RXF | FEC_ENET_RXB) diff --git a/drivers/net/fs_enet/mac-scc.c b/drivers/net/fs_enet/mac-scc.c index e3557eca7b6d..029b3c7ef29c 100644 --- a/drivers/net/fs_enet/mac-scc.c +++ b/drivers/net/fs_enet/mac-scc.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -43,10 +44,6 @@ #include #endif -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include -#endif - #include "fs_enet.h" /*************************************************/ @@ -99,7 +96,6 @@ static inline int scc_cr_cmd(struct fs_enet_private *fep, u32 op) static int do_pd_setup(struct fs_enet_private *fep) { -#ifdef CONFIG_PPC_CPM_NEW_BINDING struct of_device *ofdev = to_of_device(fep->dev); fep->interrupt = of_irq_to_resource(ofdev->node, 0, NULL); @@ -115,27 +111,6 @@ static int do_pd_setup(struct fs_enet_private *fep) iounmap(fep->scc.sccp); return -EINVAL; } -#else - struct platform_device *pdev = to_platform_device(fep->dev); - struct resource *r; - - /* Fill out IRQ field */ - fep->interrupt = platform_get_irq_byname(pdev, "interrupt"); - if (fep->interrupt < 0) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - fep->scc.sccp = ioremap(r->start, r->end - r->start + 1); - - if (fep->scc.sccp == NULL) - return -EINVAL; - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pram"); - fep->scc.ep = ioremap(r->start, r->end - r->start + 1); - - if (fep->scc.ep == NULL) - return -EINVAL; -#endif return 0; } @@ -149,16 +124,6 @@ static int setup_data(struct net_device *dev) { struct fs_enet_private *fep = netdev_priv(dev); -#ifndef CONFIG_PPC_CPM_NEW_BINDING - struct fs_platform_info *fpi = fep->fpi; - - fep->scc.idx = fs_get_scc_index(fpi->fs_no); - if ((unsigned int)fep->fcc.idx >= 4) /* max 4 SCCs */ - return -EINVAL; - - fpi->cp_command = fep->fcc.idx << 6; -#endif - do_pd_setup(fep); fep->scc.hthi = 0; diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c index 1620030cd33c..be4b72f4f49a 100644 --- a/drivers/net/fs_enet/mii-bitbang.c +++ b/drivers/net/fs_enet/mii-bitbang.c @@ -22,10 +22,7 @@ #include #include #include - -#ifdef CONFIG_PPC_CPM_NEW_BINDING #include -#endif #include "fs_enet.h" @@ -110,7 +107,6 @@ static struct mdiobb_ops bb_ops = { .get_mdio_data = mdio_read, }; -#ifdef CONFIG_PPC_CPM_NEW_BINDING static int __devinit fs_mii_bitbang_init(struct mii_bus *bus, struct device_node *np) { @@ -271,106 +267,3 @@ static void fs_enet_mdio_bb_exit(void) module_init(fs_enet_mdio_bb_init); module_exit(fs_enet_mdio_bb_exit); -#else -static int __devinit fs_mii_bitbang_init(struct bb_info *bitbang, - struct fs_mii_bb_platform_info *fmpi) -{ - bitbang->dir = (u32 __iomem *)fmpi->mdio_dir.offset; - bitbang->dat = (u32 __iomem *)fmpi->mdio_dat.offset; - bitbang->mdio_msk = 1U << (31 - fmpi->mdio_dat.bit); - bitbang->mdc_msk = 1U << (31 - fmpi->mdc_dat.bit); - - return 0; -} - -static int __devinit fs_enet_mdio_probe(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct fs_mii_bb_platform_info *pdata; - struct mii_bus *new_bus; - struct bb_info *bitbang; - int err = 0; - - if (NULL == dev) - return -EINVAL; - - bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL); - - if (NULL == bitbang) - return -ENOMEM; - - bitbang->ctrl.ops = &bb_ops; - - new_bus = alloc_mdio_bitbang(&bitbang->ctrl); - - if (NULL == new_bus) - return -ENOMEM; - - new_bus->name = "BB MII Bus", - snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id); - - new_bus->phy_mask = ~0x9; - pdata = (struct fs_mii_bb_platform_info *)pdev->dev.platform_data; - - if (NULL == pdata) { - printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id); - return -ENODEV; - } - - /*set up workspace*/ - fs_mii_bitbang_init(bitbang, pdata); - - new_bus->priv = bitbang; - - new_bus->irq = pdata->irq; - - new_bus->dev = dev; - dev_set_drvdata(dev, new_bus); - - err = mdiobus_register(new_bus); - - if (0 != err) { - printk (KERN_ERR "%s: Cannot register as MDIO bus\n", - new_bus->name); - goto bus_register_fail; - } - - return 0; - -bus_register_fail: - free_mdio_bitbang(new_bus); - kfree(bitbang); - - return err; -} - -static int fs_enet_mdio_remove(struct device *dev) -{ - struct mii_bus *bus = dev_get_drvdata(dev); - - mdiobus_unregister(bus); - - dev_set_drvdata(dev, NULL); - - free_mdio_bitbang(bus); - - return 0; -} - -static struct device_driver fs_enet_bb_mdio_driver = { - .name = "fsl-bb-mdio", - .bus = &platform_bus_type, - .probe = fs_enet_mdio_probe, - .remove = fs_enet_mdio_remove, -}; - -int fs_enet_mdio_bb_init(void) -{ - return driver_register(&fs_enet_bb_mdio_driver); -} - -void fs_enet_mdio_bb_exit(void) -{ - driver_unregister(&fs_enet_bb_mdio_driver); -} -#endif diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c index 8f6a43b0e0ff..695f74cc4398 100644 --- a/drivers/net/fs_enet/mii-fec.c +++ b/drivers/net/fs_enet/mii-fec.c @@ -31,15 +31,12 @@ #include #include #include +#include #include #include #include -#ifdef CONFIG_PPC_CPM_NEW_BINDING -#include -#endif - #include "fs_enet.h" #include "fec.h" @@ -51,52 +48,6 @@ #define FEC_MII_LOOPS 10000 -#ifndef CONFIG_PPC_CPM_NEW_BINDING -static int match_has_phy (struct device *dev, void* data) -{ - struct platform_device* pdev = container_of(dev, struct platform_device, dev); - struct fs_platform_info* fpi; - if(strcmp(pdev->name, (char*)data)) - { - return 0; - } - - fpi = pdev->dev.platform_data; - if((fpi)&&(fpi->has_phy)) - return 1; - return 0; -} - -static int fs_mii_fec_init(struct fec_info* fec, struct fs_mii_fec_platform_info *fmpi) -{ - struct resource *r; - fec_t __iomem *fecp; - char* name = "fsl-cpm-fec"; - - /* we need fec in order to be useful */ - struct platform_device *fec_pdev = - container_of(bus_find_device(&platform_bus_type, NULL, name, match_has_phy), - struct platform_device, dev); - - if(fec_pdev == NULL) { - printk(KERN_ERR"Unable to find PHY for %s", name); - return -ENODEV; - } - - r = platform_get_resource_byname(fec_pdev, IORESOURCE_MEM, "regs"); - - fec->fecp = fecp = ioremap(r->start,sizeof(fec_t)); - fec->mii_speed = fmpi->mii_speed; - - setbits32(&fecp->fec_r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */ - setbits32(&fecp->fec_ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN); - out_be32(&fecp->fec_ievent, FEC_ENET_MII); - out_be32(&fecp->fec_mii_speed, fec->mii_speed); - - return 0; -} -#endif - static int fs_enet_fec_mii_read(struct mii_bus *bus , int phy_id, int location) { struct fec_info* fec = bus->priv; @@ -151,7 +102,6 @@ static int fs_enet_fec_mii_reset(struct mii_bus *bus) return 0; } -#ifdef CONFIG_PPC_CPM_NEW_BINDING static void __devinit add_phy(struct mii_bus *bus, struct device_node *np) { const u32 *data; @@ -286,95 +236,3 @@ static void fs_enet_mdio_fec_exit(void) module_init(fs_enet_mdio_fec_init); module_exit(fs_enet_mdio_fec_exit); -#else -static int __devinit fs_enet_fec_mdio_probe(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct fs_mii_fec_platform_info *pdata; - struct mii_bus *new_bus; - struct fec_info *fec; - int err = 0; - if (NULL == dev) - return -EINVAL; - new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); - - if (NULL == new_bus) - return -ENOMEM; - - fec = kzalloc(sizeof(struct fec_info), GFP_KERNEL); - - if (NULL == fec) - return -ENOMEM; - - new_bus->name = "FEC MII Bus", - new_bus->read = &fs_enet_fec_mii_read, - new_bus->write = &fs_enet_fec_mii_write, - new_bus->reset = &fs_enet_fec_mii_reset, - snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id); - - pdata = (struct fs_mii_fec_platform_info *)pdev->dev.platform_data; - - if (NULL == pdata) { - printk(KERN_ERR "fs_enet FEC mdio %d: Missing platform data!\n", pdev->id); - return -ENODEV; - } - - /*set up workspace*/ - - fs_mii_fec_init(fec, pdata); - new_bus->priv = fec; - - new_bus->irq = pdata->irq; - - new_bus->dev = dev; - dev_set_drvdata(dev, new_bus); - - err = mdiobus_register(new_bus); - - if (0 != err) { - printk (KERN_ERR "%s: Cannot register as MDIO bus\n", - new_bus->name); - goto bus_register_fail; - } - - return 0; - -bus_register_fail: - kfree(new_bus); - - return err; -} - - -static int fs_enet_fec_mdio_remove(struct device *dev) -{ - struct mii_bus *bus = dev_get_drvdata(dev); - - mdiobus_unregister(bus); - - dev_set_drvdata(dev, NULL); - kfree(bus->priv); - - bus->priv = NULL; - kfree(bus); - - return 0; -} - -static struct device_driver fs_enet_fec_mdio_driver = { - .name = "fsl-cpm-fec-mdio", - .bus = &platform_bus_type, - .probe = fs_enet_fec_mdio_probe, - .remove = fs_enet_fec_mdio_remove, -}; - -int fs_enet_mdio_fec_init(void) -{ - return driver_register(&fs_enet_fec_mdio_driver); -} - -void fs_enet_mdio_fec_exit(void) -{ - driver_unregister(&fs_enet_fec_mdio_driver); -} -#endif diff --git a/include/linux/fs_enet_pd.h b/include/linux/fs_enet_pd.h index 9bc045b8c478..0ba21ee0f58c 100644 --- a/include/linux/fs_enet_pd.h +++ b/include/linux/fs_enet_pd.h @@ -135,11 +135,7 @@ struct fs_platform_info { u32 device_flags; int phy_addr; /* the phy address (-1 no phy) */ -#ifdef CONFIG_PPC_CPM_NEW_BINDING char bus_id[16]; -#else - const char* bus_id; -#endif int phy_irq; /* the phy irq (if it exists) */ const struct fs_mii_bus_info *bus_info; -- cgit From d2c52b7983b95bb3fc2a784e479f832f142d4523 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Jul 2008 17:59:55 -0700 Subject: async_tx: export async_tx_quiesce Replace open coded "wait and acknowledge" instances with async_tx_quiesce. Signed-off-by: Dan Williams --- crypto/async_tx/async_memcpy.c | 10 +--------- crypto/async_tx/async_memset.c | 10 +--------- crypto/async_tx/async_tx.c | 29 ++++++++++++++++++++--------- crypto/async_tx/async_xor.c | 37 ++----------------------------------- include/linux/async_tx.h | 2 ++ 5 files changed, 26 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/crypto/async_tx/async_memcpy.c b/crypto/async_tx/async_memcpy.c index a5eda80e8427..06a7f4be9736 100644 --- a/crypto/async_tx/async_memcpy.c +++ b/crypto/async_tx/async_memcpy.c @@ -73,15 +73,7 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, pr_debug("%s: (sync) len: %zu\n", __func__, len); /* wait for any prerequisite operations */ - if (depend_tx) { - /* if ack is already set then we cannot be sure - * we are referring to the correct operation - */ - BUG_ON(async_tx_test_ack(depend_tx)); - if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for depend_tx\n", - __func__); - } + async_tx_quiesce(&depend_tx); dest_buf = kmap_atomic(dest, KM_USER0) + dest_offset; src_buf = kmap_atomic(src, KM_USER1) + src_offset; diff --git a/crypto/async_tx/async_memset.c b/crypto/async_tx/async_memset.c index 27a97dc90a7e..d48ed22ed1c3 100644 --- a/crypto/async_tx/async_memset.c +++ b/crypto/async_tx/async_memset.c @@ -72,15 +72,7 @@ async_memset(struct page *dest, int val, unsigned int offset, dest_buf = (void *) (((char *) page_address(dest)) + offset); /* wait for any prerequisite operations */ - if (depend_tx) { - /* if ack is already set then we cannot be sure - * we are referring to the correct operation - */ - BUG_ON(async_tx_test_ack(depend_tx)); - if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for depend_tx\n", - __func__); - } + async_tx_quiesce(&depend_tx); memset(dest_buf, val, len); diff --git a/crypto/async_tx/async_tx.c b/crypto/async_tx/async_tx.c index 9325c61208a0..78a61e7f631a 100644 --- a/crypto/async_tx/async_tx.c +++ b/crypto/async_tx/async_tx.c @@ -607,15 +607,7 @@ async_trigger_callback(enum async_tx_flags flags, pr_debug("%s: (sync)\n", __func__); /* wait for any prerequisite operations */ - if (depend_tx) { - /* if ack is already set then we cannot be sure - * we are referring to the correct operation - */ - BUG_ON(async_tx_test_ack(depend_tx)); - if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for depend_tx\n", - __func__); - } + async_tx_quiesce(&depend_tx); async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); } @@ -624,6 +616,25 @@ async_trigger_callback(enum async_tx_flags flags, } EXPORT_SYMBOL_GPL(async_trigger_callback); +/** + * async_tx_quiesce - ensure tx is complete and freeable upon return + * @tx - transaction to quiesce + */ +void async_tx_quiesce(struct dma_async_tx_descriptor **tx) +{ + if (*tx) { + /* if ack is already set then we cannot be sure + * we are referring to the correct operation + */ + BUG_ON(async_tx_test_ack(*tx)); + if (dma_wait_for_async_tx(*tx) == DMA_ERROR) + panic("DMA_ERROR waiting for transaction\n"); + async_tx_ack(*tx); + *tx = NULL; + } +} +EXPORT_SYMBOL_GPL(async_tx_quiesce); + module_init(async_tx_init); module_exit(async_tx_exit); diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c index 19d16e452bcc..689ecce73ee1 100644 --- a/crypto/async_tx/async_xor.c +++ b/crypto/async_tx/async_xor.c @@ -30,24 +30,6 @@ #include #include -/** - * async_tx_quiesce - ensure tx is complete and freeable upon return - * @tx - transaction to quiesce - */ -static void async_tx_quiesce(struct dma_async_tx_descriptor **tx) -{ - if (*tx) { - /* if ack is already set then we cannot be sure - * we are referring to the correct operation - */ - BUG_ON(async_tx_test_ack(*tx)); - if (dma_wait_for_async_tx(*tx) == DMA_ERROR) - panic("DMA_ERROR waiting for transaction\n"); - async_tx_ack(*tx); - *tx = NULL; - } -} - /* do_async_xor - dma map the pages and perform the xor with an engine. * This routine is marked __always_inline so it can be compiled away * when CONFIG_DMA_ENGINE=n @@ -219,15 +201,7 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset, } /* wait for any prerequisite operations */ - if (depend_tx) { - /* if ack is already set then we cannot be sure - * we are referring to the correct operation - */ - BUG_ON(async_tx_test_ack(depend_tx)); - if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for depend_tx\n", - __func__); - } + async_tx_quiesce(&depend_tx); do_sync_xor(dest, src_list, offset, src_cnt, len, flags, depend_tx, cb_fn, cb_param); @@ -309,17 +283,10 @@ async_xor_zero_sum(struct page *dest, struct page **src_list, tx = async_xor(dest, src_list, offset, src_cnt, len, xor_flags, depend_tx, NULL, NULL); - if (tx) { - if (dma_wait_for_async_tx(tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for tx\n", - __func__); - async_tx_ack(tx); - } + async_tx_quiesce(&tx); *result = page_is_zero(dest, offset, len) ? 0 : 1; - tx = NULL; - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); } diff --git a/include/linux/async_tx.h b/include/linux/async_tx.h index eb640f0acfac..9f0e7bd5bdc9 100644 --- a/include/linux/async_tx.h +++ b/include/linux/async_tx.h @@ -152,4 +152,6 @@ struct dma_async_tx_descriptor * async_trigger_callback(enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_fn_param); + +void async_tx_quiesce(struct dma_async_tx_descriptor **tx); #endif /* _ASYNC_TX_H_ */ -- cgit From 3dce01713723bbcc92562bd4488e8b840a4f786c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Jul 2008 17:59:55 -0700 Subject: async_tx: remove depend_tx from async_tx_sync_epilog All callers of async_tx_sync_epilog have called async_tx_quiesce on the depend_tx, so async_tx_sync_epilog need only call the callback to complete the operation. Signed-off-by: Dan Williams --- crypto/async_tx/async_memcpy.c | 2 +- crypto/async_tx/async_memset.c | 2 +- crypto/async_tx/async_tx.c | 2 +- crypto/async_tx/async_xor.c | 7 +++---- include/linux/async_tx.h | 9 +-------- 5 files changed, 7 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/crypto/async_tx/async_memcpy.c b/crypto/async_tx/async_memcpy.c index 06a7f4be9736..ddccfb01c416 100644 --- a/crypto/async_tx/async_memcpy.c +++ b/crypto/async_tx/async_memcpy.c @@ -83,7 +83,7 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, kunmap_atomic(dest_buf, KM_USER0); kunmap_atomic(src_buf, KM_USER1); - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); + async_tx_sync_epilog(cb_fn, cb_param); } return tx; diff --git a/crypto/async_tx/async_memset.c b/crypto/async_tx/async_memset.c index d48ed22ed1c3..5b5eb99bb244 100644 --- a/crypto/async_tx/async_memset.c +++ b/crypto/async_tx/async_memset.c @@ -76,7 +76,7 @@ async_memset(struct page *dest, int val, unsigned int offset, memset(dest_buf, val, len); - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); + async_tx_sync_epilog(cb_fn, cb_param); } return tx; diff --git a/crypto/async_tx/async_tx.c b/crypto/async_tx/async_tx.c index 78a61e7f631a..35869a37a6f2 100644 --- a/crypto/async_tx/async_tx.c +++ b/crypto/async_tx/async_tx.c @@ -609,7 +609,7 @@ async_trigger_callback(enum async_tx_flags flags, /* wait for any prerequisite operations */ async_tx_quiesce(&depend_tx); - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); + async_tx_sync_epilog(cb_fn, cb_param); } return tx; diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c index 689ecce73ee1..65974c6d3d7a 100644 --- a/crypto/async_tx/async_xor.c +++ b/crypto/async_tx/async_xor.c @@ -121,7 +121,6 @@ do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list, static void do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset, int src_cnt, size_t len, enum async_tx_flags flags, - struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_param) { int i; @@ -150,7 +149,7 @@ do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset, src_off += xor_src_cnt; } - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); + async_tx_sync_epilog(cb_fn, cb_param); } /** @@ -204,7 +203,7 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset, async_tx_quiesce(&depend_tx); do_sync_xor(dest, src_list, offset, src_cnt, len, - flags, depend_tx, cb_fn, cb_param); + flags, cb_fn, cb_param); return NULL; } @@ -287,7 +286,7 @@ async_xor_zero_sum(struct page *dest, struct page **src_list, *result = page_is_zero(dest, offset, len) ? 0 : 1; - async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); + async_tx_sync_epilog(cb_fn, cb_param); } return tx; diff --git a/include/linux/async_tx.h b/include/linux/async_tx.h index 9f0e7bd5bdc9..0f50d4cc4360 100644 --- a/include/linux/async_tx.h +++ b/include/linux/async_tx.h @@ -101,21 +101,14 @@ async_tx_find_channel(struct dma_async_tx_descriptor *depend_tx, /** * async_tx_sync_epilog - actions to take if an operation is run synchronously - * @flags: async_tx flags - * @depend_tx: transaction depends on depend_tx * @cb_fn: function to call when the transaction completes * @cb_fn_param: parameter to pass to the callback routine */ static inline void -async_tx_sync_epilog(unsigned long flags, - struct dma_async_tx_descriptor *depend_tx, - dma_async_tx_callback cb_fn, void *cb_fn_param) +async_tx_sync_epilog(dma_async_tx_callback cb_fn, void *cb_fn_param) { if (cb_fn) cb_fn(cb_fn_param); - - if (depend_tx && (flags & ASYNC_TX_DEP_ACK)) - async_tx_ack(depend_tx); } void -- cgit From 0839875e0c197ded56bbae820e699f26d6fa2697 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Jul 2008 17:59:56 -0700 Subject: async_tx: make async_tx_test_ack a boolean routine Signed-off-by: Dan Williams --- include/linux/dmaengine.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 9b91d341e1fa..adb0b084eb5a 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -375,16 +375,14 @@ dma_cookie_t dma_async_memcpy_pg_to_pg(struct dma_chan *chan, void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx, struct dma_chan *chan); -static inline void -async_tx_ack(struct dma_async_tx_descriptor *tx) +static inline void async_tx_ack(struct dma_async_tx_descriptor *tx) { tx->flags |= DMA_CTRL_ACK; } -static inline int -async_tx_test_ack(struct dma_async_tx_descriptor *tx) +static inline bool async_tx_test_ack(struct dma_async_tx_descriptor *tx) { - return tx->flags & DMA_CTRL_ACK; + return (tx->flags & DMA_CTRL_ACK) == DMA_CTRL_ACK; } #define first_dma_cap(mask) __first_dma_cap(&(mask)) -- cgit From e761b7725234276a802322549cee5255305a0930 Mon Sep 17 00:00:00 2001 From: Max Krasnyansky Date: Tue, 15 Jul 2008 04:43:49 -0700 Subject: cpu hotplug, sched: Introduce cpu_active_map and redo sched domain managment (take 2) This is based on Linus' idea of creating cpu_active_map that prevents scheduler load balancer from migrating tasks to the cpu that is going down. It allows us to simplify domain management code and avoid unecessary domain rebuilds during cpu hotplug event handling. Please ignore the cpusets part for now. It needs some more work in order to avoid crazy lock nesting. Although I did simplfy and unify domain reinitialization logic. We now simply call partition_sched_domains() in all the cases. This means that we're using exact same code paths as in cpusets case and hence the test below cover cpusets too. Cpuset changes to make rebuild_sched_domains() callable from various contexts are in the separate patch (right next after this one). This not only boots but also easily handles while true; do make clean; make -j 8; done and while true; do on-off-cpu 1; done at the same time. (on-off-cpu 1 simple does echo 0/1 > /sys/.../cpu1/online thing). Suprisingly the box (dual-core Core2) is quite usable. In fact I'm typing this on right now in gnome-terminal and things are moving just fine. Also this is running with most of the debug features enabled (lockdep, mutex, etc) no BUG_ONs or lockdep complaints so far. I believe I addressed all of the Dmitry's comments for original Linus' version. I changed both fair and rt balancer to mask out non-active cpus. And replaced cpu_is_offline() with !cpu_active() in the main scheduler code where it made sense (to me). Signed-off-by: Max Krasnyanskiy Acked-by: Linus Torvalds Acked-by: Peter Zijlstra Acked-by: Gregory Haskins Cc: dmitry.adamushko@gmail.com Cc: pj@sgi.com Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 6 ++- include/linux/cpuset.h | 7 ++++ init/main.c | 7 ++++ kernel/cpu.c | 30 +++++++++++--- kernel/cpuset.c | 2 +- kernel/sched.c | 108 ++++++++++++++++++++---------------------------- kernel/sched_fair.c | 3 ++ kernel/sched_rt.c | 7 ++++ 8 files changed, 99 insertions(+), 71 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index c24875bd9c5b..d614d2472798 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -359,13 +359,14 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp, /* * The following particular system cpumasks and operations manage - * possible, present and online cpus. Each of them is a fixed size + * possible, present, active and online cpus. Each of them is a fixed size * bitmap of size NR_CPUS. * * #ifdef CONFIG_HOTPLUG_CPU * cpu_possible_map - has bit 'cpu' set iff cpu is populatable * cpu_present_map - has bit 'cpu' set iff cpu is populated * cpu_online_map - has bit 'cpu' set iff cpu available to scheduler + * cpu_active_map - has bit 'cpu' set iff cpu available to migration * #else * cpu_possible_map - has bit 'cpu' set iff cpu is populated * cpu_present_map - copy of cpu_possible_map @@ -416,6 +417,7 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp, extern cpumask_t cpu_possible_map; extern cpumask_t cpu_online_map; extern cpumask_t cpu_present_map; +extern cpumask_t cpu_active_map; #if NR_CPUS > 1 #define num_online_cpus() cpus_weight(cpu_online_map) @@ -424,6 +426,7 @@ extern cpumask_t cpu_present_map; #define cpu_online(cpu) cpu_isset((cpu), cpu_online_map) #define cpu_possible(cpu) cpu_isset((cpu), cpu_possible_map) #define cpu_present(cpu) cpu_isset((cpu), cpu_present_map) +#define cpu_active(cpu) cpu_isset((cpu), cpu_active_map) #else #define num_online_cpus() 1 #define num_possible_cpus() 1 @@ -431,6 +434,7 @@ extern cpumask_t cpu_present_map; #define cpu_online(cpu) ((cpu) == 0) #define cpu_possible(cpu) ((cpu) == 0) #define cpu_present(cpu) ((cpu) == 0) +#define cpu_active(cpu) ((cpu) == 0) #endif #define cpu_is_offline(cpu) unlikely(!cpu_online(cpu)) diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 038578362b47..e8f450c499b0 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -78,6 +78,8 @@ extern void cpuset_track_online_nodes(void); extern int current_cpuset_is_being_rebound(void); +extern void rebuild_sched_domains(void); + #else /* !CONFIG_CPUSETS */ static inline int cpuset_init_early(void) { return 0; } @@ -156,6 +158,11 @@ static inline int current_cpuset_is_being_rebound(void) return 0; } +static inline void rebuild_sched_domains(void) +{ + partition_sched_domains(0, NULL, NULL); +} + #endif /* !CONFIG_CPUSETS */ #endif /* _LINUX_CPUSET_H */ diff --git a/init/main.c b/init/main.c index edeace036fd9..dd25259530ea 100644 --- a/init/main.c +++ b/init/main.c @@ -415,6 +415,13 @@ static void __init smp_init(void) { unsigned int cpu; + /* + * Set up the current CPU as possible to migrate to. + * The other ones will be done by cpu_up/cpu_down() + */ + cpu = smp_processor_id(); + cpu_set(cpu, cpu_active_map); + /* FIXME: This should be done in userspace --RR */ for_each_present_cpu(cpu) { if (num_online_cpus() >= setup_max_cpus) diff --git a/kernel/cpu.c b/kernel/cpu.c index cfb1d43ab801..a1ac7ea245d7 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -64,6 +64,8 @@ void __init cpu_hotplug_init(void) cpu_hotplug.refcount = 0; } +cpumask_t cpu_active_map; + #ifdef CONFIG_HOTPLUG_CPU void get_online_cpus(void) @@ -291,11 +293,20 @@ int __ref cpu_down(unsigned int cpu) int err = 0; cpu_maps_update_begin(); - if (cpu_hotplug_disabled) + + if (cpu_hotplug_disabled) { err = -EBUSY; - else - err = _cpu_down(cpu, 0); + goto out; + } + + cpu_clear(cpu, cpu_active_map); + + err = _cpu_down(cpu, 0); + + if (cpu_online(cpu)) + cpu_set(cpu, cpu_active_map); +out: cpu_maps_update_done(); return err; } @@ -355,11 +366,18 @@ int __cpuinit cpu_up(unsigned int cpu) } cpu_maps_update_begin(); - if (cpu_hotplug_disabled) + + if (cpu_hotplug_disabled) { err = -EBUSY; - else - err = _cpu_up(cpu, 0); + goto out; + } + err = _cpu_up(cpu, 0); + + if (cpu_online(cpu)) + cpu_set(cpu, cpu_active_map); + +out: cpu_maps_update_done(); return err; } diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 459d601947a8..3c3ef02f65f1 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -564,7 +564,7 @@ update_domain_attr(struct sched_domain_attr *dattr, struct cpuset *c) * partition_sched_domains(). */ -static void rebuild_sched_domains(void) +void rebuild_sched_domains(void) { struct kfifo *q; /* queue of cpusets to be scanned */ struct cpuset *cp; /* scans q */ diff --git a/kernel/sched.c b/kernel/sched.c index 1ee18dbb4516..c237624a8a04 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2881,7 +2881,7 @@ static void sched_migrate_task(struct task_struct *p, int dest_cpu) rq = task_rq_lock(p, &flags); if (!cpu_isset(dest_cpu, p->cpus_allowed) - || unlikely(cpu_is_offline(dest_cpu))) + || unlikely(!cpu_active(dest_cpu))) goto out; /* force the process onto the specified CPU */ @@ -3849,7 +3849,7 @@ int select_nohz_load_balancer(int stop_tick) /* * If we are going offline and still the leader, give up! */ - if (cpu_is_offline(cpu) && + if (!cpu_active(cpu) && atomic_read(&nohz.load_balancer) == cpu) { if (atomic_cmpxchg(&nohz.load_balancer, cpu, -1) != cpu) BUG(); @@ -5876,7 +5876,7 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) struct rq *rq_dest, *rq_src; int ret = 0, on_rq; - if (unlikely(cpu_is_offline(dest_cpu))) + if (unlikely(!cpu_active(dest_cpu))) return ret; rq_src = cpu_rq(src_cpu); @@ -7553,18 +7553,6 @@ void __attribute__((weak)) arch_update_cpu_topology(void) { } -/* - * Free current domain masks. - * Called after all cpus are attached to NULL domain. - */ -static void free_sched_domains(void) -{ - ndoms_cur = 0; - if (doms_cur != &fallback_doms) - kfree(doms_cur); - doms_cur = &fallback_doms; -} - /* * Set up scheduler domains and groups. Callers must hold the hotplug lock. * For now this just excludes isolated cpus, but could be used to @@ -7643,7 +7631,7 @@ static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur, * ownership of it and will kfree it when done with it. If the caller * failed the kmalloc call, then it can pass in doms_new == NULL, * and partition_sched_domains() will fallback to the single partition - * 'fallback_doms'. + * 'fallback_doms', it also forces the domains to be rebuilt. * * Call with hotplug lock held */ @@ -7657,12 +7645,8 @@ void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, /* always unregister in case we don't destroy any domains */ unregister_sched_domain_sysctl(); - if (doms_new == NULL) { - ndoms_new = 1; - doms_new = &fallback_doms; - cpus_andnot(doms_new[0], cpu_online_map, cpu_isolated_map); - dattr_new = NULL; - } + if (doms_new == NULL) + ndoms_new = 0; /* Destroy deleted domains */ for (i = 0; i < ndoms_cur; i++) { @@ -7677,6 +7661,14 @@ match1: ; } + if (doms_new == NULL) { + ndoms_cur = 0; + ndoms_new = 1; + doms_new = &fallback_doms; + cpus_andnot(doms_new[0], cpu_online_map, cpu_isolated_map); + dattr_new = NULL; + } + /* Build new domains */ for (i = 0; i < ndoms_new; i++) { for (j = 0; j < ndoms_cur; j++) { @@ -7707,17 +7699,10 @@ match2: #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) int arch_reinit_sched_domains(void) { - int err; - get_online_cpus(); - mutex_lock(&sched_domains_mutex); - detach_destroy_domains(&cpu_online_map); - free_sched_domains(); - err = arch_init_sched_domains(&cpu_online_map); - mutex_unlock(&sched_domains_mutex); + rebuild_sched_domains(); put_online_cpus(); - - return err; + return 0; } static ssize_t sched_power_savings_store(const char *buf, size_t count, int smt) @@ -7783,14 +7768,30 @@ int sched_create_sysfs_power_savings_entries(struct sysdev_class *cls) } #endif /* CONFIG_SCHED_MC || CONFIG_SCHED_SMT */ +#ifndef CONFIG_CPUSETS /* - * Force a reinitialization of the sched domains hierarchy. The domains - * and groups cannot be updated in place without racing with the balancing - * code, so we temporarily attach all running cpus to the NULL domain - * which will prevent rebalancing while the sched domains are recalculated. + * Add online and remove offline CPUs from the scheduler domains. + * When cpusets are enabled they take over this function. */ static int update_sched_domains(struct notifier_block *nfb, unsigned long action, void *hcpu) +{ + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + case CPU_DEAD: + case CPU_DEAD_FROZEN: + partition_sched_domains(0, NULL, NULL); + return NOTIFY_OK; + + default: + return NOTIFY_DONE; + } +} +#endif + +static int update_runtime(struct notifier_block *nfb, + unsigned long action, void *hcpu) { int cpu = (int)(long)hcpu; @@ -7798,44 +7799,18 @@ static int update_sched_domains(struct notifier_block *nfb, case CPU_DOWN_PREPARE: case CPU_DOWN_PREPARE_FROZEN: disable_runtime(cpu_rq(cpu)); - /* fall-through */ - case CPU_UP_PREPARE: - case CPU_UP_PREPARE_FROZEN: - detach_destroy_domains(&cpu_online_map); - free_sched_domains(); return NOTIFY_OK; - case CPU_DOWN_FAILED: case CPU_DOWN_FAILED_FROZEN: case CPU_ONLINE: case CPU_ONLINE_FROZEN: enable_runtime(cpu_rq(cpu)); - /* fall-through */ - case CPU_UP_CANCELED: - case CPU_UP_CANCELED_FROZEN: - case CPU_DEAD: - case CPU_DEAD_FROZEN: - /* - * Fall through and re-initialise the domains. - */ - break; + return NOTIFY_OK; + default: return NOTIFY_DONE; } - -#ifndef CONFIG_CPUSETS - /* - * Create default domain partitioning if cpusets are disabled. - * Otherwise we let cpusets rebuild the domains based on the - * current setup. - */ - - /* The hotplug lock is already held by cpu_up/cpu_down */ - arch_init_sched_domains(&cpu_online_map); -#endif - - return NOTIFY_OK; } void __init sched_init_smp(void) @@ -7855,8 +7830,15 @@ void __init sched_init_smp(void) cpu_set(smp_processor_id(), non_isolated_cpus); mutex_unlock(&sched_domains_mutex); put_online_cpus(); + +#ifndef CONFIG_CPUSETS /* XXX: Theoretical race here - CPU may be hotplugged now */ hotcpu_notifier(update_sched_domains, 0); +#endif + + /* RT runtime code needs to handle some hotplug events */ + hotcpu_notifier(update_runtime, 0); + init_hrtick(); /* Move init over to a non-isolated CPU */ diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index f2aa987027d6..d924c679dfac 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -1004,6 +1004,8 @@ static void yield_task_fair(struct rq *rq) * not idle and an idle cpu is available. The span of cpus to * search starts with cpus closest then further out as needed, * so we always favor a closer, idle cpu. + * Domains may include CPUs that are not usable for migration, + * hence we need to mask them out (cpu_active_map) * * Returns the CPU we should wake onto. */ @@ -1031,6 +1033,7 @@ static int wake_idle(int cpu, struct task_struct *p) || ((sd->flags & SD_WAKE_IDLE_FAR) && !task_hot(p, task_rq(p)->clock, sd))) { cpus_and(tmp, sd->span, p->cpus_allowed); + cpus_and(tmp, tmp, cpu_active_map); for_each_cpu_mask(i, tmp) { if (idle_cpu(i)) { if (i != task_cpu(p)) { diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c index d3d1cccb3d7b..50735bb96149 100644 --- a/kernel/sched_rt.c +++ b/kernel/sched_rt.c @@ -933,6 +933,13 @@ static int find_lowest_rq(struct task_struct *task) if (!cpupri_find(&task_rq(task)->rd->cpupri, task, lowest_mask)) return -1; /* No targets found */ + /* + * Only consider CPUs that are usable for migration. + * I guess we might want to change cpupri_find() to ignore those + * in the first place. + */ + cpus_and(*lowest_mask, *lowest_mask, cpu_active_map); + /* * At this point we have built a mask of cpus representing the * lowest priority tasks in the system. Now we want to elect -- cgit From 1b427c153a08fdbc092c2bdbf845b92fda58d857 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 18 Jul 2008 14:01:39 +0200 Subject: sched: fix build error, provide partition_sched_domains() unconditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit provide an empty partition_sched_domains() definition for the UP case: include/linux/cpuset.h: In function ‘rebuild_sched_domains': include/linux/cpuset.h:163: error: implicit declaration of function ‘partition_sched_domains' Signed-off-by: Ingo Molnar --- include/linux/sched.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 1941d8b5cf11..26da921530fe 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -824,7 +824,16 @@ extern void partition_sched_domains(int ndoms_new, cpumask_t *doms_new, struct sched_domain_attr *dattr_new); extern int arch_reinit_sched_domains(void); -#endif /* CONFIG_SMP */ +#else /* CONFIG_SMP */ + +struct sched_domain_attr; + +static inline void +partition_sched_domains(int ndoms_new, cpumask_t *doms_new, + struct sched_domain_attr *dattr_new) +{ +} +#endif /* !CONFIG_SMP */ struct io_context; /* See blkdev.h */ #define NGROUPS_SMALL 32 -- cgit From b8f8c3cf0a4ac0632ec3f0e15e9dc0c29de917af Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 18 Jul 2008 17:27:28 +0200 Subject: nohz: prevent tick stop outside of the idle loop Jack Ren and Eric Miao tracked down the following long standing problem in the NOHZ code: scheduler switch to idle task enable interrupts Window starts here ----> interrupt happens (does not set NEED_RESCHED) irq_exit() stops the tick ----> interrupt happens (does set NEED_RESCHED) return from schedule() cpu_idle(): preempt_disable(); Window ends here The interrupts can happen at any point inside the race window. The first interrupt stops the tick, the second one causes the scheduler to rerun and switch away from idle again and we end up with the tick disabled. The fact that it needs two interrupts where the first one does not set NEED_RESCHED and the second one does made the bug obscure and extremly hard to reproduce and analyse. Kudos to Jack and Eric. Solution: Limit the NOHZ functionality to the idle loop to make sure that we can not run into such a situation ever again. cpu_idle() { preempt_disable(); while(1) { tick_nohz_stop_sched_tick(1); <- tell NOHZ code that we are in the idle loop while (!need_resched()) halt(); tick_nohz_restart_sched_tick(); <- disables NOHZ mode preempt_enable_no_resched(); schedule(); preempt_disable(); } } In hindsight we should have done this forever, but ... /me grabs a large brown paperbag. Debugged-by: Jack Ren , Debugged-by: eric miao Signed-off-by: Thomas Gleixner --- arch/arm/kernel/process.c | 2 +- arch/avr32/kernel/process.c | 2 +- arch/blackfin/kernel/process.c | 2 +- arch/mips/kernel/process.c | 2 +- arch/powerpc/kernel/idle.c | 2 +- arch/powerpc/platforms/iseries/setup.c | 4 ++-- arch/sh/kernel/process_32.c | 2 +- arch/sparc64/kernel/process.c | 2 +- arch/um/kernel/process.c | 2 +- arch/x86/kernel/process_32.c | 2 +- arch/x86/kernel/process_64.c | 2 +- include/linux/tick.h | 5 +++-- kernel/softirq.c | 2 +- kernel/time/tick-sched.c | 12 ++++++++++-- 14 files changed, 26 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 46bf2ede6128..84f5a4c778fb 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -164,7 +164,7 @@ void cpu_idle(void) if (!idle) idle = default_idle; leds_event(led_idle_start); - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) idle(); leds_event(led_idle_end); diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c index 6cf9df176274..ff820a9e743a 100644 --- a/arch/avr32/kernel/process.c +++ b/arch/avr32/kernel/process.c @@ -31,7 +31,7 @@ void cpu_idle(void) { /* endless idle loop with no priority at all */ while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) cpu_idle_sleep(); tick_nohz_restart_sched_tick(); diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c index 53c2cd255441..77800dd83e57 100644 --- a/arch/blackfin/kernel/process.c +++ b/arch/blackfin/kernel/process.c @@ -105,7 +105,7 @@ void cpu_idle(void) #endif if (!idle) idle = default_idle; - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) idle(); tick_nohz_restart_sched_tick(); diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 2c09a442e5e5..bdead3aad253 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -53,7 +53,7 @@ void __noreturn cpu_idle(void) { /* endless idle loop with no priority at all */ while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) { #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG extern void smtc_idle_loop_hook(void); diff --git a/arch/powerpc/kernel/idle.c b/arch/powerpc/kernel/idle.c index c3cf0e8f3ac1..d308a9f70f1b 100644 --- a/arch/powerpc/kernel/idle.c +++ b/arch/powerpc/kernel/idle.c @@ -60,7 +60,7 @@ void cpu_idle(void) set_thread_flag(TIF_POLLING_NRFLAG); while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched() && !cpu_should_die()) { ppc64_runlatch_off(); diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index b72120751bbe..70b688c1aefb 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c @@ -561,7 +561,7 @@ static void yield_shared_processor(void) static void iseries_shared_idle(void) { while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched() && !hvlpevent_is_pending()) { local_irq_disable(); ppc64_runlatch_off(); @@ -591,7 +591,7 @@ static void iseries_dedicated_idle(void) set_thread_flag(TIF_POLLING_NRFLAG); while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); if (!need_resched()) { while (!need_resched()) { ppc64_runlatch_off(); diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c index b98e37a1f54c..921892c351da 100644 --- a/arch/sh/kernel/process_32.c +++ b/arch/sh/kernel/process_32.c @@ -86,7 +86,7 @@ void cpu_idle(void) if (!idle) idle = default_idle; - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) idle(); tick_nohz_restart_sched_tick(); diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 2084f81a76e1..0798928ba361 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -97,7 +97,7 @@ void cpu_idle(void) set_thread_flag(TIF_POLLING_NRFLAG); while(1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched() && !cpu_is_offline(cpu)) sparc64_yield(cpu); diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index 83603cfbde81..a1c6d07cac3e 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -243,7 +243,7 @@ void default_idle(void) if (need_resched()) schedule(); - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); nsecs = disable_timer(); idle_sleep(nsecs); tick_nohz_restart_sched_tick(); diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index f8476dfbb60d..1f5fa1cf16dd 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -166,7 +166,7 @@ void cpu_idle(void) /* endless idle loop with no priority at all */ while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) { void (*idle)(void); diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index e2319f39988b..c0a5c2a687e6 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -148,7 +148,7 @@ void cpu_idle(void) current_thread_info()->status |= TS_POLLING; /* endless idle loop with no priority at all */ while (1) { - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(1); while (!need_resched()) { void (*idle)(void); diff --git a/include/linux/tick.h b/include/linux/tick.h index a881c652f7e9..d3c02695dc5d 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -49,6 +49,7 @@ struct tick_sched { unsigned long check_clocks; enum tick_nohz_mode nohz_mode; ktime_t idle_tick; + int inidle; int tick_stopped; unsigned long idle_jiffies; unsigned long idle_calls; @@ -105,14 +106,14 @@ static inline int tick_check_oneshot_change(int allow_nohz) { return 0; } #endif /* !CONFIG_GENERIC_CLOCKEVENTS */ # ifdef CONFIG_NO_HZ -extern void tick_nohz_stop_sched_tick(void); +extern void tick_nohz_stop_sched_tick(int inidle); extern void tick_nohz_restart_sched_tick(void); extern void tick_nohz_update_jiffies(void); extern ktime_t tick_nohz_get_sleep_length(void); extern void tick_nohz_stop_idle(int cpu); extern u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time); # else -static inline void tick_nohz_stop_sched_tick(void) { } +static inline void tick_nohz_stop_sched_tick(int inidle) { } static inline void tick_nohz_restart_sched_tick(void) { } static inline void tick_nohz_update_jiffies(void) { } static inline ktime_t tick_nohz_get_sleep_length(void) diff --git a/kernel/softirq.c b/kernel/softirq.c index 36e061740047..05f248039d77 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -312,7 +312,7 @@ void irq_exit(void) #ifdef CONFIG_NO_HZ /* Make sure that timer wheel updates are propagated */ if (!in_interrupt() && idle_cpu(smp_processor_id()) && !need_resched()) - tick_nohz_stop_sched_tick(); + tick_nohz_stop_sched_tick(0); rcu_irq_exit(); #endif preempt_enable_no_resched(); diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 86baa4f0dfe4..ee962d11107b 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -195,7 +195,7 @@ u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time) * Called either from the idle loop or from irq_exit() when an idle period was * just interrupted by an interrupt which did not cause a reschedule. */ -void tick_nohz_stop_sched_tick(void) +void tick_nohz_stop_sched_tick(int inidle) { unsigned long seq, last_jiffies, next_jiffies, delta_jiffies, flags; struct tick_sched *ts; @@ -224,6 +224,11 @@ void tick_nohz_stop_sched_tick(void) if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE)) goto end; + if (!inidle && !ts->inidle) + goto end; + + ts->inidle = 1; + if (need_resched()) goto end; @@ -372,11 +377,14 @@ void tick_nohz_restart_sched_tick(void) local_irq_disable(); tick_nohz_stop_idle(cpu); - if (!ts->tick_stopped) { + if (!ts->inidle || !ts->tick_stopped) { + ts->inidle = 0; local_irq_enable(); return; } + ts->inidle = 0; + rcu_exit_nohz(); /* Update jiffies first */ -- cgit From 65c011845316d3c1381f478ca0d8265c43b3b039 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Tue, 15 Jul 2008 14:14:30 -0700 Subject: cpumask: Replace cpumask_of_cpu with cpumask_of_cpu_ptr * This patch replaces the dangerous lvalue version of cpumask_of_cpu with new cpumask_of_cpu_ptr macros. These are patterned after the node_to_cpumask_ptr macros. In general terms, if there is a cpumask_of_cpu_map[] then a pointer to the cpumask_of_cpu_map[cpu] entry is used. The cpumask_of_cpu_map is provided when there is a large NR_CPUS count, reducing greatly the amount of code generated and stack space used for cpumask_of_cpu(). The pointer to the cpumask_t value is needed for calling set_cpus_allowed_ptr() to reduce the amount of stack space needed to pass the cpumask_t value. If there isn't a cpumask_of_cpu_map[], then a temporary variable is declared and filled in with value from cpumask_of_cpu(cpu) as well as a pointer variable pointing to this temporary variable. Afterwards, the pointer is used to reference the cpumask value. The compiler will optimize out the extra dereference through the pointer as well as the stack space used for the pointer, resulting in identical code. A good example of the orthogonal usages is in net/sunrpc/svc.c: case SVC_POOL_PERCPU: { unsigned int cpu = m->pool_to[pidx]; cpumask_of_cpu_ptr(cpumask, cpu); *oldmask = current->cpus_allowed; set_cpus_allowed_ptr(current, cpumask); return 1; } case SVC_POOL_PERNODE: { unsigned int node = m->pool_to[pidx]; node_to_cpumask_ptr(nodecpumask, node); *oldmask = current->cpus_allowed; set_cpus_allowed_ptr(current, nodecpumask); return 1; } Signed-off-by: Mike Travis Signed-off-by: Ingo Molnar --- arch/x86/kernel/acpi/cstate.c | 3 ++- arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | 10 +++++--- arch/x86/kernel/cpu/cpufreq/powernow-k8.c | 15 +++++++---- arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c | 9 ++++--- arch/x86/kernel/cpu/cpufreq/speedstep-ich.c | 3 ++- arch/x86/kernel/cpu/intel_cacheinfo.c | 3 ++- arch/x86/kernel/microcode.c | 13 +++++++--- arch/x86/kernel/reboot.c | 14 +++++++---- drivers/acpi/processor_throttling.c | 11 +++++--- drivers/firmware/dcdbas.c | 3 ++- include/linux/cpumask.h | 32 ++++++++++++++++++++---- kernel/stop_machine.c | 3 ++- kernel/trace/trace_sysprof.c | 4 ++- net/sunrpc/svc.c | 3 ++- 14 files changed, 91 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/acpi/cstate.c b/arch/x86/kernel/acpi/cstate.c index c2502eb9aa83..9220cf46aa10 100644 --- a/arch/x86/kernel/acpi/cstate.c +++ b/arch/x86/kernel/acpi/cstate.c @@ -73,6 +73,7 @@ int acpi_processor_ffh_cstate_probe(unsigned int cpu, struct cpuinfo_x86 *c = &cpu_data(cpu); cpumask_t saved_mask; + cpumask_of_cpu_ptr(new_mask, cpu); int retval; unsigned int eax, ebx, ecx, edx; unsigned int edx_part; @@ -91,7 +92,7 @@ int acpi_processor_ffh_cstate_probe(unsigned int cpu, /* Make sure we are running on right CPU */ saved_mask = current->cpus_allowed; - retval = set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + retval = set_cpus_allowed_ptr(current, new_mask); if (retval) return -1; diff --git a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c index dd097b835839..ff2fff56f0a8 100644 --- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -200,10 +200,12 @@ static void drv_read(struct drv_cmd *cmd) static void drv_write(struct drv_cmd *cmd) { cpumask_t saved_mask = current->cpus_allowed; + cpumask_of_cpu_ptr_declare(cpu_mask); unsigned int i; for_each_cpu_mask_nr(i, cmd->mask) { - set_cpus_allowed_ptr(current, &cpumask_of_cpu(i)); + cpumask_of_cpu_ptr_next(cpu_mask, i); + set_cpus_allowed_ptr(current, cpu_mask); do_drv_write(cmd); } @@ -267,11 +269,12 @@ static unsigned int get_measured_perf(unsigned int cpu) } aperf_cur, mperf_cur; cpumask_t saved_mask; + cpumask_of_cpu_ptr(cpu_mask, cpu); unsigned int perf_percent; unsigned int retval; saved_mask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, cpu_mask); if (get_cpu() != cpu) { /* We were not able to run on requested processor */ put_cpu(); @@ -337,6 +340,7 @@ static unsigned int get_measured_perf(unsigned int cpu) static unsigned int get_cur_freq_on_cpu(unsigned int cpu) { + cpumask_of_cpu_ptr(cpu_mask, cpu); struct acpi_cpufreq_data *data = per_cpu(drv_data, cpu); unsigned int freq; unsigned int cached_freq; @@ -349,7 +353,7 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu) } cached_freq = data->freq_table[data->acpi_data->state].frequency; - freq = extract_freq(get_cur_val(&cpumask_of_cpu(cpu)), data); + freq = extract_freq(get_cur_val(cpu_mask), data); if (freq != cached_freq) { /* * The dreaded BIOS frequency change behind our back. diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c index c45ca6d4dce1..53c7b6936973 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c @@ -479,11 +479,12 @@ static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvi static int check_supported_cpu(unsigned int cpu) { cpumask_t oldmask; + cpumask_of_cpu_ptr(cpu_mask, cpu); u32 eax, ebx, ecx, edx; unsigned int rc = 0; oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, cpu_mask); if (smp_processor_id() != cpu) { printk(KERN_ERR PFX "limiting to cpu %u failed\n", cpu); @@ -1016,6 +1017,7 @@ static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned i static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsigned relation) { cpumask_t oldmask; + cpumask_of_cpu_ptr(cpu_mask, pol->cpu); struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu); u32 checkfid; u32 checkvid; @@ -1030,7 +1032,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi /* only run on specific CPU from here on */ oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(pol->cpu)); + set_cpus_allowed_ptr(current, cpu_mask); if (smp_processor_id() != pol->cpu) { printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu); @@ -1105,6 +1107,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) { struct powernow_k8_data *data; cpumask_t oldmask; + cpumask_of_cpu_ptr_declare(newmask); int rc; if (!cpu_online(pol->cpu)) @@ -1156,7 +1159,8 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) /* only run on specific CPU from here on */ oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(pol->cpu)); + cpumask_of_cpu_ptr_next(newmask, pol->cpu); + set_cpus_allowed_ptr(current, newmask); if (smp_processor_id() != pol->cpu) { printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu); @@ -1178,7 +1182,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) set_cpus_allowed_ptr(current, &oldmask); if (cpu_family == CPU_HW_PSTATE) - pol->cpus = cpumask_of_cpu(pol->cpu); + pol->cpus = *newmask; else pol->cpus = per_cpu(cpu_core_map, pol->cpu); data->available_cores = &(pol->cpus); @@ -1244,6 +1248,7 @@ static unsigned int powernowk8_get (unsigned int cpu) { struct powernow_k8_data *data; cpumask_t oldmask = current->cpus_allowed; + cpumask_of_cpu_ptr(newmask, cpu); unsigned int khz = 0; unsigned int first; @@ -1253,7 +1258,7 @@ static unsigned int powernowk8_get (unsigned int cpu) if (!data) return -EINVAL; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, newmask); if (smp_processor_id() != cpu) { printk(KERN_ERR PFX "limiting to CPU %d failed in powernowk8_get\n", cpu); diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c index 8b0dd6f2a1ac..fd561bb26c60 100644 --- a/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c +++ b/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c @@ -313,9 +313,10 @@ static unsigned int get_cur_freq(unsigned int cpu) unsigned l, h; unsigned clock_freq; cpumask_t saved_mask; + cpumask_of_cpu_ptr(new_mask, cpu); saved_mask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, new_mask); if (smp_processor_id() != cpu) return 0; @@ -554,9 +555,11 @@ static int centrino_target (struct cpufreq_policy *policy, */ if (!cpus_empty(covered_cpus)) { + cpumask_of_cpu_ptr_declare(new_mask); + for_each_cpu_mask_nr(j, covered_cpus) { - set_cpus_allowed_ptr(current, - &cpumask_of_cpu(j)); + cpumask_of_cpu_ptr_next(new_mask, j); + set_cpus_allowed_ptr(current, new_mask); wrmsr(MSR_IA32_PERF_CTL, oldmsr, h); } } diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c b/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c index 191f7263c61d..2f3728dc24f6 100644 --- a/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c +++ b/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c @@ -244,7 +244,8 @@ static unsigned int _speedstep_get(const cpumask_t *cpus) static unsigned int speedstep_get(unsigned int cpu) { - return _speedstep_get(&cpumask_of_cpu(cpu)); + cpumask_of_cpu_ptr(newmask, cpu); + return _speedstep_get(newmask); } /** diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c index a7b0f8f1736b..e4b8d189d7ed 100644 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c @@ -516,6 +516,7 @@ static int __cpuinit detect_cache_attributes(unsigned int cpu) unsigned long j; int retval; cpumask_t oldmask; + cpumask_of_cpu_ptr(newmask, cpu); if (num_cache_leaves == 0) return -ENOENT; @@ -526,7 +527,7 @@ static int __cpuinit detect_cache_attributes(unsigned int cpu) return -ENOMEM; oldmask = current->cpus_allowed; - retval = set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + retval = set_cpus_allowed_ptr(current, newmask); if (retval) goto out; diff --git a/arch/x86/kernel/microcode.c b/arch/x86/kernel/microcode.c index 56b933119a04..58520169e35d 100644 --- a/arch/x86/kernel/microcode.c +++ b/arch/x86/kernel/microcode.c @@ -388,6 +388,7 @@ static int do_microcode_update (void) void *new_mc = NULL; int cpu; cpumask_t old; + cpumask_of_cpu_ptr_declare(newmask); old = current->cpus_allowed; @@ -404,7 +405,8 @@ static int do_microcode_update (void) if (!uci->valid) continue; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + cpumask_of_cpu_ptr_next(newmask, cpu); + set_cpus_allowed_ptr(current, newmask); error = get_maching_microcode(new_mc, cpu); if (error < 0) goto out; @@ -574,6 +576,7 @@ static int apply_microcode_check_cpu(int cpu) struct cpuinfo_x86 *c = &cpu_data(cpu); struct ucode_cpu_info *uci = ucode_cpu_info + cpu; cpumask_t old; + cpumask_of_cpu_ptr(newmask, cpu); unsigned int val[2]; int err = 0; @@ -582,7 +585,7 @@ static int apply_microcode_check_cpu(int cpu) return 0; old = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, newmask); /* Check if the microcode we have in memory matches the CPU */ if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || @@ -620,11 +623,12 @@ static int apply_microcode_check_cpu(int cpu) static void microcode_init_cpu(int cpu, int resume) { cpumask_t old; + cpumask_of_cpu_ptr(newmask, cpu); struct ucode_cpu_info *uci = ucode_cpu_info + cpu; old = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, newmask); mutex_lock(µcode_mutex); collect_cpu_info(cpu); if (uci->valid && system_state == SYSTEM_RUNNING && !resume) @@ -656,11 +660,12 @@ static ssize_t reload_store(struct sys_device *dev, const char *buf, size_t sz) return -EINVAL; if (val == 1) { cpumask_t old; + cpumask_of_cpu_ptr(newmask, cpu); old = current->cpus_allowed; get_online_cpus(); - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, newmask); mutex_lock(µcode_mutex); if (uci->valid) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index f8a62160e151..214bbdfc851e 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -403,24 +403,28 @@ void native_machine_shutdown(void) { /* Stop the cpus and apics */ #ifdef CONFIG_SMP - int reboot_cpu_id; /* The boot cpu is always logical cpu 0 */ - reboot_cpu_id = 0; + int reboot_cpu_id = 0; + cpumask_of_cpu_ptr(newmask, reboot_cpu_id); #ifdef CONFIG_X86_32 /* See if there has been given a command line override */ if ((reboot_cpu != -1) && (reboot_cpu < NR_CPUS) && - cpu_online(reboot_cpu)) + cpu_online(reboot_cpu)) { reboot_cpu_id = reboot_cpu; + cpumask_of_cpu_ptr_next(newmask, reboot_cpu_id); + } #endif /* Make certain the cpu I'm about to reboot on is online */ - if (!cpu_online(reboot_cpu_id)) + if (!cpu_online(reboot_cpu_id)) { reboot_cpu_id = smp_processor_id(); + cpumask_of_cpu_ptr_next(newmask, reboot_cpu_id); + } /* Make certain I only run on the appropriate processor */ - set_cpus_allowed_ptr(current, &cpumask_of_cpu(reboot_cpu_id)); + set_cpus_allowed_ptr(current, newmask); /* O.K Now that I'm on the appropriate processor, * stop all of the others. diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index a56fc6c4394b..a2c3f9cfa549 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -827,6 +827,7 @@ static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr) static int acpi_processor_get_throttling(struct acpi_processor *pr) { cpumask_t saved_mask; + cpumask_of_cpu_ptr_declare(new_mask); int ret; if (!pr) @@ -838,7 +839,8 @@ static int acpi_processor_get_throttling(struct acpi_processor *pr) * Migrate task to the cpu pointed by pr. */ saved_mask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(pr->id)); + cpumask_of_cpu_ptr_next(new_mask, pr->id); + set_cpus_allowed_ptr(current, new_mask); ret = pr->throttling.acpi_processor_get_throttling(pr); /* restore the previous state */ set_cpus_allowed_ptr(current, &saved_mask); @@ -987,6 +989,7 @@ static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr, int acpi_processor_set_throttling(struct acpi_processor *pr, int state) { cpumask_t saved_mask; + cpumask_of_cpu_ptr_declare(new_mask); int ret = 0; unsigned int i; struct acpi_processor *match_pr; @@ -1025,7 +1028,8 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) * it can be called only for the cpu pointed by pr. */ if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) { - set_cpus_allowed_ptr(current, &cpumask_of_cpu(pr->id)); + cpumask_of_cpu_ptr_next(new_mask, pr->id); + set_cpus_allowed_ptr(current, new_mask); ret = p_throttling->acpi_processor_set_throttling(pr, t_state.target_state); } else { @@ -1056,7 +1060,8 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state) continue; } t_state.cpu = i; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(i)); + cpumask_of_cpu_ptr_next(new_mask, i); + set_cpus_allowed_ptr(current, new_mask); ret = match_pr->throttling. acpi_processor_set_throttling( match_pr, t_state.target_state); diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 25918f7dfd0f..0b624e927a6f 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -254,6 +254,7 @@ static ssize_t host_control_on_shutdown_store(struct device *dev, static int smi_request(struct smi_cmd *smi_cmd) { cpumask_t old_mask; + cpumask_of_cpu_ptr(new_mask, 0); int ret = 0; if (smi_cmd->magic != SMI_CMD_MAGIC) { @@ -264,7 +265,7 @@ static int smi_request(struct smi_cmd *smi_cmd) /* SMI requires CPU 0 */ old_mask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(0)); + set_cpus_allowed_ptr(current, new_mask); if (smp_processor_id() != 0) { dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n", __func__); diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 80226e776143..2dbd9a287e77 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -62,6 +62,15 @@ * int next_cpu_nr(cpu, mask) Next cpu past 'cpu', or nr_cpu_ids * * cpumask_t cpumask_of_cpu(cpu) Return cpumask with bit 'cpu' set + *ifdef CONFIG_HAS_CPUMASK_OF_CPU + * cpumask_of_cpu_ptr_declare(v) Declares cpumask_t *v + * cpumask_of_cpu_ptr_next(v, cpu) Sets v = &cpumask_of_cpu_map[cpu] + * cpumask_of_cpu_ptr(v, cpu) Combines above two operations + *else + * cpumask_of_cpu_ptr_declare(v) Declares cpumask_t _v and *v = &_v + * cpumask_of_cpu_ptr_next(v, cpu) Sets _v = cpumask_of_cpu(cpu) + * cpumask_of_cpu_ptr(v, cpu) Combines above two operations + *endif * CPU_MASK_ALL Initializer - all bits set * CPU_MASK_NONE Initializer - no bits set * unsigned long *cpus_addr(mask) Array of unsigned long's in mask @@ -236,11 +245,16 @@ static inline void __cpus_shift_left(cpumask_t *dstp, #ifdef CONFIG_HAVE_CPUMASK_OF_CPU_MAP extern cpumask_t *cpumask_of_cpu_map; -#define cpumask_of_cpu(cpu) (cpumask_of_cpu_map[cpu]) - +#define cpumask_of_cpu(cpu) (cpumask_of_cpu_map[cpu]) +#define cpumask_of_cpu_ptr(v, cpu) \ + const cpumask_t *v = &cpumask_of_cpu(cpu) +#define cpumask_of_cpu_ptr_declare(v) \ + const cpumask_t *v +#define cpumask_of_cpu_ptr_next(v, cpu) \ + v = &cpumask_of_cpu(cpu) #else #define cpumask_of_cpu(cpu) \ -(*({ \ +({ \ typeof(_unused_cpumask_arg_) m; \ if (sizeof(m) == sizeof(unsigned long)) { \ m.bits[0] = 1UL<<(cpu); \ @@ -248,8 +262,16 @@ extern cpumask_t *cpumask_of_cpu_map; cpus_clear(m); \ cpu_set((cpu), m); \ } \ - &m; \ -})) + m; \ +}) +#define cpumask_of_cpu_ptr(v, cpu) \ + cpumask_t _##v = cpumask_of_cpu(cpu); \ + const cpumask_t *v = &_##v +#define cpumask_of_cpu_ptr_declare(v) \ + cpumask_t _##v; \ + const cpumask_t *v = &_##v +#define cpumask_of_cpu_ptr_next(v, cpu) \ + _##v = cpumask_of_cpu(cpu) #endif #define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS) diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index ba9b2054ecbd..738b411ff2d3 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -33,8 +33,9 @@ static int stopmachine(void *cpu) { int irqs_disabled = 0; int prepared = 0; + cpumask_of_cpu_ptr(cpumask, (int)(long)cpu); - set_cpus_allowed_ptr(current, &cpumask_of_cpu((int)(long)cpu)); + set_cpus_allowed_ptr(current, cpumask); /* Ack: we are alive */ smp_mb(); /* Theoretically the ack = 0 might not be on this CPU yet. */ diff --git a/kernel/trace/trace_sysprof.c b/kernel/trace/trace_sysprof.c index 2301e1e7c606..63528086337c 100644 --- a/kernel/trace/trace_sysprof.c +++ b/kernel/trace/trace_sysprof.c @@ -213,7 +213,9 @@ static void start_stack_timers(void) int cpu; for_each_online_cpu(cpu) { - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + cpumask_of_cpu_ptr(new_mask, cpu); + + set_cpus_allowed_ptr(current, new_mask); start_stack_timer(cpu); } set_cpus_allowed_ptr(current, &saved_mask); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index d43cf8ddff67..083d12688134 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -314,9 +314,10 @@ svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask) case SVC_POOL_PERCPU: { unsigned int cpu = m->pool_to[pidx]; + cpumask_of_cpu_ptr(cpumask, cpu); *oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + set_cpus_allowed_ptr(current, cpumask); return 1; } case SVC_POOL_PERNODE: -- cgit From 77586c2bdad0798cb24e35de5a878e7c6b200574 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Tue, 15 Jul 2008 14:14:36 -0700 Subject: cpumask: Provide a generic set of CPUMASK_ALLOC macros * Provide a generic set of CPUMASK_ALLOC macros patterned after the SCHED_CPUMASK_ALLOC macros. This is used where multiple cpumask_t variables are declared on the stack to reduce the amount of stack space required. Signed-off-by: Mike Travis Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 2dbd9a287e77..72f9c32c12b0 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -75,6 +75,17 @@ * CPU_MASK_NONE Initializer - no bits set * unsigned long *cpus_addr(mask) Array of unsigned long's in mask * + *if NR_CPUS > BITS_PER_LONG + * CPUMASK_ALLOC(m) Declares and allocates struct m *m = + * (struct m *)kmalloc(sizeof(*m), ...) + * CPUMASK_FREE(m) Macro for kfree(v) + *else + * CPUMASK_ALLOC(m) Declares struct m _m, *m = &_m + * CPUMASK_FREE(m) Nop + *endif + * CPUMASK_VAR(v, m) Declares cpumask_t *v = + * m + offset(struct m, v) + * * int cpumask_scnprintf(buf, len, mask) Format cpumask for printing * int cpumask_parse_user(ubuf, ulen, mask) Parse ascii string as cpumask * int cpulist_scnprintf(buf, len, mask) Format cpumask as list for printing @@ -311,6 +322,16 @@ extern cpumask_t cpu_mask_all; #define cpus_addr(src) ((src).bits) +#if NR_CPUS > BITS_PER_LONG +#define CPUMASK_ALLOC(m) struct m *m = kmalloc(sizeof(*m), GFP_KERNEL) +#define CPUMASK_FREE(m) kfree(m) +#else +#define CPUMASK_ALLOC(m) struct allmasks _m, *m = &_m +#define CPUMASK_FREE(m) +#endif +#define CPUMASK_VAR(v, m) cpumask_t *v = (cpumask_t *) \ + ((unsigned long)(m) + offsetof(struct m, v)) + #define cpumask_scnprintf(buf, len, src) \ __cpumask_scnprintf((buf), (len), &(src), NR_CPUS) static inline int __cpumask_scnprintf(char *buf, int len, -- cgit From ae79cdaacb5599781f8bb49f4bdd5723029669cf Mon Sep 17 00:00:00 2001 From: "venkatesh.pallipadi@intel.com" Date: Fri, 18 Jul 2008 16:08:13 -0700 Subject: x86: Add a arch directory for x86 under debugfs Add a directory for x86 arch under debugfs. Can be used to accumulate all x86 specific debugfs files. Signed-off-by: Venkatesh Pallipadi Signed-off-by: H. Peter Anvin --- arch/x86/kernel/kdebugfs.c | 8 ++++++++ include/linux/debugfs.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'include/linux') diff --git a/arch/x86/kernel/kdebugfs.c b/arch/x86/kernel/kdebugfs.c index c03205991718..f2d43bc75514 100644 --- a/arch/x86/kernel/kdebugfs.c +++ b/arch/x86/kernel/kdebugfs.c @@ -12,9 +12,13 @@ #include #include #include +#include #include +struct dentry *arch_debugfs_dir; +EXPORT_SYMBOL(arch_debugfs_dir); + #ifdef CONFIG_DEBUG_BOOT_PARAMS struct setup_data_node { u64 paddr; @@ -209,6 +213,10 @@ static int __init arch_kdebugfs_init(void) { int error = 0; + arch_debugfs_dir = debugfs_create_dir("x86", NULL); + if (!arch_debugfs_dir) + return -ENOMEM; + #ifdef CONFIG_DEBUG_BOOT_PARAMS error = boot_params_kdebugfs_init(); #endif diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 7266124361b4..32755cdf68db 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -26,6 +26,8 @@ struct debugfs_blob_wrapper { unsigned long size; }; +extern struct dentry *arch_debugfs_dir; + #if defined(CONFIG_DEBUG_FS) /* declared over in file.c */ -- cgit From 80422d3431cc990b967da129f9eb8e3e9989f841 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Fri, 18 Jul 2008 18:11:33 -0700 Subject: cpumask: Provide a generic set of CPUMASK_ALLOC macros, FIXUP * Rename CPUMASK_VAR --> CPUMASK_PTR (and simplify) * Fix a semantic error in CPUMASK_ALLOC * Add a bit of commentry to cpumask.h Signed-off-by: Mike Travis Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 72f9c32c12b0..30d59d1d0626 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -75,16 +75,36 @@ * CPU_MASK_NONE Initializer - no bits set * unsigned long *cpus_addr(mask) Array of unsigned long's in mask * + * CPUMASK_ALLOC kmalloc's a structure that is a composite of many cpumask_t + * variables, and CPUMASK_PTR provides pointers to each field. + * + * The structure should be defined something like this: + * struct my_cpumasks { + * cpumask_t mask1; + * cpumask_t mask2; + * }; + * + * Usage is then: + * CPUMASK_ALLOC(my_cpumasks); + * CPUMASK_PTR(mask1, my_cpumasks); + * CPUMASK_PTR(mask2, my_cpumasks); + * + * --- DO NOT reference cpumask_t pointers until this check --- + * if (my_cpumasks == NULL) + * "kmalloc failed"... + * + * References are now pointers to the cpumask_t variables (*mask1, ...) + * *if NR_CPUS > BITS_PER_LONG * CPUMASK_ALLOC(m) Declares and allocates struct m *m = - * (struct m *)kmalloc(sizeof(*m), ...) - * CPUMASK_FREE(m) Macro for kfree(v) + * kmalloc(sizeof(*m), GFP_KERNEL) + * CPUMASK_FREE(m) Macro for kfree(m) *else * CPUMASK_ALLOC(m) Declares struct m _m, *m = &_m * CPUMASK_FREE(m) Nop *endif - * CPUMASK_VAR(v, m) Declares cpumask_t *v = - * m + offset(struct m, v) + * CPUMASK_PTR(v, m) Declares cpumask_t *v = &(m->v) + * ------------------------------------------------------------------------ * * int cpumask_scnprintf(buf, len, mask) Format cpumask for printing * int cpumask_parse_user(ubuf, ulen, mask) Parse ascii string as cpumask @@ -326,11 +346,10 @@ extern cpumask_t cpu_mask_all; #define CPUMASK_ALLOC(m) struct m *m = kmalloc(sizeof(*m), GFP_KERNEL) #define CPUMASK_FREE(m) kfree(m) #else -#define CPUMASK_ALLOC(m) struct allmasks _m, *m = &_m +#define CPUMASK_ALLOC(m) struct m _m, *m = &_m #define CPUMASK_FREE(m) #endif -#define CPUMASK_VAR(v, m) cpumask_t *v = (cpumask_t *) \ - ((unsigned long)(m) + offsetof(struct m, v)) +#define CPUMASK_PTR(v, m) cpumask_t *v = &(m->v) #define cpumask_scnprintf(buf, len, src) \ __cpumask_scnprintf((buf), (len), &(src), NR_CPUS) -- cgit From f233ea5c9e0d8b95e4283bf6a3436b88f6fd3586 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 21 Jul 2008 17:05:22 +1000 Subject: md: Make mddev->array_size sector-based. This patch renames the array_size field of struct mddev_s to array_sectors and converts all instances to use units of 512 byte sectors instead of 1k blocks. Signed-off-by: Andre Noll Signed-off-by: NeilBrown --- drivers/md/faulty.c | 2 +- drivers/md/linear.c | 6 +++--- drivers/md/md.c | 12 +++++++----- drivers/md/multipath.c | 2 +- drivers/md/raid0.c | 8 ++++---- drivers/md/raid1.c | 11 ++++++----- drivers/md/raid10.c | 2 +- drivers/md/raid5.c | 16 +++++++++------- include/linux/raid/md_k.h | 2 +- 9 files changed, 33 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c index d107ddceefcd..268547dbfbd3 100644 --- a/drivers/md/faulty.c +++ b/drivers/md/faulty.c @@ -297,7 +297,7 @@ static int run(mddev_t *mddev) rdev_for_each(rdev, tmp, mddev) conf->rdev = rdev; - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->private = conf; reconfig(mddev, mddev->layout, -1); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index ec921f58fbb8..57644a780f16 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -256,7 +256,7 @@ static int linear_run (mddev_t *mddev) if (!conf) return 1; mddev->private = conf; - mddev->array_size = conf->array_size; + mddev->array_sectors = conf->array_size * 2; blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); mddev->queue->unplug_fn = linear_unplug; @@ -290,8 +290,8 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) newconf->prev = mddev_to_conf(mddev); mddev->private = newconf; mddev->raid_disks++; - mddev->array_size = newconf->array_size; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = newconf->array_size * 2; + set_capacity(mddev->gendisk, mddev->array_sectors); return 0; } diff --git a/drivers/md/md.c b/drivers/md/md.c index df13a17a9627..4bfbc1982cda 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3704,7 +3704,7 @@ static int do_md_run(mddev_t * mddev) if (mddev->flags) md_update_sb(mddev, 0); - set_capacity(disk, mddev->array_size<<1); + set_capacity(disk, mddev->array_sectors); /* If we call blk_queue_make_request here, it will * re-initialise max_sectors etc which may have been @@ -3905,7 +3905,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) export_array(mddev); - mddev->array_size = 0; + mddev->array_sectors = 0; mddev->size = 0; mddev->raid_disks = 0; mddev->recovery_cp = 0; @@ -4644,7 +4644,8 @@ static int update_size(mddev_t *mddev, sector_t num_sectors) bdev = bdget_disk(mddev->gendisk, 0); if (bdev) { mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, (loff_t)mddev->array_size << 10); + i_size_write(bdev->bd_inode, + (loff_t)mddev->array_sectors << 9); mutex_unlock(&bdev->bd_inode->i_mutex); bdput(bdev); } @@ -5391,10 +5392,11 @@ static int md_seq_show(struct seq_file *seq, void *v) if (!list_empty(&mddev->disks)) { if (mddev->pers) seq_printf(seq, "\n %llu blocks", - (unsigned long long)mddev->array_size); + (unsigned long long) + mddev->array_sectors / 2); else seq_printf(seq, "\n %llu blocks", - (unsigned long long)size); + (unsigned long long)size); } if (mddev->persistent) { if (mddev->major_version != 0 || diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 541cbe3414bd..c4779ccba1c3 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -504,7 +504,7 @@ static int multipath_run (mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->queue->unplug_fn = multipath_unplug; mddev->queue->backing_dev_info.congested_fn = multipath_congested; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 914c04ddec7c..2f30ebd8b7ab 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -293,16 +293,16 @@ static int raid0_run (mddev_t *mddev) goto out_free_conf; /* calculate array device size */ - mddev->array_size = 0; + mddev->array_sectors = 0; rdev_for_each(rdev, tmp, mddev) - mddev->array_size += rdev->size; + mddev->array_sectors += rdev->size * 2; printk("raid0 : md_size is %llu blocks.\n", - (unsigned long long)mddev->array_size); + (unsigned long long)mddev->array_sectors / 2); printk("raid0 : conf->hash_spacing is %llu blocks.\n", (unsigned long long)conf->hash_spacing); { - sector_t s = mddev->array_size; + sector_t s = mddev->array_sectors / 2; sector_t space = conf->hash_spacing; int round; conf->preshift = 0; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 491dc2d4ad5f..03a5ab705c20 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2043,7 +2043,7 @@ static int run(mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->queue->unplug_fn = raid1_unplug; mddev->queue->backing_dev_info.congested_fn = raid1_congested; @@ -2105,14 +2105,15 @@ static int raid1_resize(mddev_t *mddev, sector_t sectors) * any io in the removed space completes, but it hardly seems * worth it. */ - mddev->array_size = sectors>>1; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = sectors; + set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; - if (mddev->array_size > mddev->size && mddev->recovery_cp == MaxSector) { + if (mddev->array_sectors / 2 > mddev->size && + mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->size << 1; set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); } - mddev->size = mddev->array_size; + mddev->size = mddev->array_sectors / 2; mddev->resync_max_sectors = sectors; return 0; } diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index df08a9fa3a1f..2acea4025243 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2164,7 +2164,7 @@ static int run(mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = size << (conf->chunk_shift-1); + mddev->array_sectors = size << conf->chunk_shift; mddev->resync_max_sectors = size << conf->chunk_shift; mddev->queue->unplug_fn = raid10_unplug; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 8f4c70a53210..42a480ba767b 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3540,7 +3540,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped j == raid6_next_disk(sh->pd_idx, sh->disks)) continue; s = compute_blocknr(sh, j); - if (s < (mddev->array_size<<1)) { + if (s < mddev->array_sectors) { skipped = 1; continue; } @@ -4189,7 +4189,7 @@ static int run(mddev_t *mddev) mddev->queue->backing_dev_info.congested_data = mddev; mddev->queue->backing_dev_info.congested_fn = raid5_congested; - mddev->array_size = mddev->size * (conf->previous_raid_disks - + mddev->array_sectors = 2 * mddev->size * (conf->previous_raid_disks - conf->max_degraded); blk_queue_merge_bvec(mddev->queue, raid5_mergeable_bvec); @@ -4413,8 +4413,9 @@ static int raid5_resize(mddev_t *mddev, sector_t sectors) raid5_conf_t *conf = mddev_to_conf(mddev); sectors &= ~((sector_t)mddev->chunk_size/512 - 1); - mddev->array_size = (sectors * (mddev->raid_disks-conf->max_degraded))>>1; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = sectors * (mddev->raid_disks + - conf->max_degraded); + set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; if (sectors/2 > mddev->size && mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->size << 1; @@ -4547,15 +4548,16 @@ static void end_reshape(raid5_conf_t *conf) struct block_device *bdev; if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) { - conf->mddev->array_size = conf->mddev->size * + conf->mddev->array_sectors = 2 * conf->mddev->size * (conf->raid_disks - conf->max_degraded); - set_capacity(conf->mddev->gendisk, conf->mddev->array_size << 1); + set_capacity(conf->mddev->gendisk, conf->mddev->array_sectors); conf->mddev->changed = 1; bdev = bdget_disk(conf->mddev->gendisk, 0); if (bdev) { mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, (loff_t)conf->mddev->array_size << 10); + i_size_write(bdev->bd_inode, + (loff_t)conf->mddev->array_sectors << 9); mutex_unlock(&bdev->bd_inode->i_mutex); bdput(bdev); } diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index e37aaa41efc6..6f72b47ae41c 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -150,7 +150,7 @@ struct mddev_s int raid_disks; int max_disks; sector_t size; /* used size of component devices */ - sector_t array_size; /* exported array size */ + sector_t array_sectors; /* exported array size */ __u64 events; char uuid[16]; -- cgit From d6e2215052810678bc9782fd980b52706fc71f50 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 21 Jul 2008 17:05:25 +1000 Subject: md: linear: Make array_size sector-based and rename it to array_sectors. Signed-off-by: Andre Noll Signed-off-by: NeilBrown --- drivers/md/linear.c | 16 ++++++++-------- include/linux/raid/linear.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 57644a780f16..1cafaa959443 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -120,7 +120,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) return NULL; cnt = 0; - conf->array_size = 0; + conf->array_sectors = 0; rdev_for_each(rdev, tmp, mddev) { int j = rdev->raid_disk; @@ -144,7 +144,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9); disk->size = rdev->size; - conf->array_size += rdev->size; + conf->array_sectors += rdev->size * 2; cnt++; } @@ -153,7 +153,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) goto out; } - min_spacing = conf->array_size; + min_spacing = conf->array_sectors / 2; sector_div(min_spacing, PAGE_SIZE/sizeof(struct dev_info *)); /* min_spacing is the minimum spacing that will fit the hash @@ -162,7 +162,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) * that is larger than min_spacing as use the size of that as * the actual spacing */ - conf->hash_spacing = conf->array_size; + conf->hash_spacing = conf->array_sectors / 2; for (i=0; i < cnt-1 ; i++) { sector_t sz = 0; int j; @@ -192,7 +192,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) unsigned round; unsigned long base; - sz = conf->array_size >> conf->preshift; + sz = conf->array_sectors >> (conf->preshift + 1); sz += 1; /* force round-up */ base = conf->hash_spacing >> conf->preshift; round = sector_div(sz, base); @@ -219,7 +219,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) curr_offset = 0; i = 0; for (curr_offset = 0; - curr_offset < conf->array_size; + curr_offset < conf->array_sectors / 2; curr_offset += conf->hash_spacing) { while (i < raid_disks-1 && @@ -256,7 +256,7 @@ static int linear_run (mddev_t *mddev) if (!conf) return 1; mddev->private = conf; - mddev->array_sectors = conf->array_size * 2; + mddev->array_sectors = conf->array_sectors; blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); mddev->queue->unplug_fn = linear_unplug; @@ -290,7 +290,7 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) newconf->prev = mddev_to_conf(mddev); mddev->private = newconf; mddev->raid_disks++; - mddev->array_sectors = newconf->array_size * 2; + mddev->array_sectors = newconf->array_sectors; set_capacity(mddev->gendisk, mddev->array_sectors); return 0; } diff --git a/include/linux/raid/linear.h b/include/linux/raid/linear.h index ba15469daf11..7e375111d007 100644 --- a/include/linux/raid/linear.h +++ b/include/linux/raid/linear.h @@ -16,7 +16,7 @@ struct linear_private_data struct linear_private_data *prev; /* earlier version */ dev_info_t **hash_table; sector_t hash_spacing; - sector_t array_size; + sector_t array_sectors; int preshift; /* shift before dividing by hash_spacing */ dev_info_t disks[0]; }; -- cgit From f2ea68cf42aafdd93393b6b8b20fc3c2b5f4390c Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 21 Jul 2008 17:05:25 +1000 Subject: md: only count actual openers as access which prevent a 'stop' Open isn't the only thing that increments ->active. e.g. reading /proc/mdstat will increment it briefly. So to avoid false positives in testing for concurrent access, introduce a new counter that counts just the number of times the md device it open. Signed-off-by: NeilBrown --- drivers/md/md.c | 9 ++++++--- include/linux/raid/md_k.h | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index 4bfbc1982cda..450f30b6edf9 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -273,6 +273,7 @@ static mddev_t * mddev_find(dev_t unit) INIT_LIST_HEAD(&new->all_mddevs); init_timer(&new->safemode_timer); atomic_set(&new->active, 1); + atomic_set(&new->openers, 0); spin_lock_init(&new->write_lock); init_waitqueue_head(&new->sb_wait); init_waitqueue_head(&new->recovery_wait); @@ -2695,14 +2696,14 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) break; case clear: /* stopping an active array */ - if (atomic_read(&mddev->active) > 1) + if (atomic_read(&mddev->openers) > 0) return -EBUSY; err = do_md_stop(mddev, 0, 0); break; case inactive: /* stopping an active array */ if (mddev->pers) { - if (atomic_read(&mddev->active) > 1) + if (atomic_read(&mddev->openers) > 0) return -EBUSY; err = do_md_stop(mddev, 2, 0); } else @@ -3816,7 +3817,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) int err = 0; struct gendisk *disk = mddev->gendisk; - if (atomic_read(&mddev->active) > 1 + is_open) { + if (atomic_read(&mddev->openers) > is_open) { printk("md: %s still in use.\n",mdname(mddev)); return -EBUSY; } @@ -5014,6 +5015,7 @@ static int md_open(struct inode *inode, struct file *file) err = 0; mddev_get(mddev); + atomic_inc(&mddev->openers); mddev_unlock(mddev); check_disk_change(inode->i_bdev); @@ -5026,6 +5028,7 @@ static int md_release(struct inode *inode, struct file * file) mddev_t *mddev = inode->i_bdev->bd_disk->private_data; BUG_ON(!mddev); + atomic_dec(&mddev->openers); mddev_put(mddev); return 0; diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 6f72b47ae41c..4bef4791d80d 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -215,7 +215,8 @@ struct mddev_s int in_sync; /* know to not need resync */ struct mutex reconfig_mutex; - atomic_t active; + atomic_t active; /* general refcount */ + atomic_t openers; /* number of active opens */ int changed; /* true if we might need to reread partition info */ int degraded; /* whether md should consider -- cgit From 4b80991c6cb9efa607bc4fd6f3ecdf5511c31bb0 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 21 Jul 2008 17:05:25 +1000 Subject: md: Protect access to mddev->disks list using RCU All modifications and most access to the mddev->disks list are made under the reconfig_mutex lock. However there are three places where the list is walked without any locking. If a reconfig happens at this time, havoc (and oops) can ensue. So use RCU to protect these accesses: - wrap them in rcu_read_{,un}lock() - use list_for_each_entry_rcu - add to the list with list_add_rcu - delete from the list with list_del_rcu - delay the 'free' with call_rcu rather than schedule_work Note that export_rdev did a list_del_init on this list. In almost all cases the entry was not in the list anymore so it was a no-op and so safe. It is no longer safe as after list_del_rcu we may not touch the list_head. An audit shows that export_rdev is called: - after unbind_rdev_from_array, in which case the delete has already been done, - after bind_rdev_to_array fails, in which case the delete isn't needed. - before the device has been put on a list at all (e.g. in add_new_disk where reading the superblock fails). - and in autorun devices after a failure when the device is on a different list. So remove the list_del_init call from export_rdev, and add it back immediately before the called to export_rdev for that last case. Note also that ->same_set is sometimes used for lists other than mddev->list (e.g. candidates). In these cases rcu is not needed. Signed-off-by: NeilBrown --- drivers/md/bitmap.c | 15 ++++++++++----- drivers/md/md.c | 30 ++++++++++++++++++------------ include/linux/raid/md_k.h | 3 +++ 3 files changed, 31 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index eba83e25b678..621a272a2c74 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -241,10 +241,10 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) { mdk_rdev_t *rdev; - struct list_head *tmp; mddev_t *mddev = bitmap->mddev; - rdev_for_each(rdev, tmp, mddev) + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev) if (test_bit(In_sync, &rdev->flags) && !test_bit(Faulty, &rdev->flags)) { int size = PAGE_SIZE; @@ -260,11 +260,11 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) + (long)(page->index * (PAGE_SIZE/512)) + size/512 > 0) /* bitmap runs in to metadata */ - return -EINVAL; + goto bad_alignment; if (rdev->data_offset + mddev->size*2 > rdev->sb_start + bitmap->offset) /* data runs in to bitmap */ - return -EINVAL; + goto bad_alignment; } else if (rdev->sb_start < rdev->data_offset) { /* METADATA BITMAP DATA */ if (rdev->sb_start @@ -272,7 +272,7 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) + page->index*(PAGE_SIZE/512) + size/512 > rdev->data_offset) /* bitmap runs in to data */ - return -EINVAL; + goto bad_alignment; } else { /* DATA METADATA BITMAP - no problems */ } @@ -282,10 +282,15 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) size, page); } + rcu_read_unlock(); if (wait) md_super_wait(mddev); return 0; + + bad_alignment: + rcu_read_unlock(); + return -EINVAL; } static void bitmap_file_kick(struct bitmap *bitmap); diff --git a/drivers/md/md.c b/drivers/md/md.c index 450f30b6edf9..c2ff77ccec50 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1395,15 +1395,17 @@ static struct super_type super_types[] = { static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) { - struct list_head *tmp, *tmp2; mdk_rdev_t *rdev, *rdev2; - rdev_for_each(rdev, tmp, mddev1) - rdev_for_each(rdev2, tmp2, mddev2) + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev1) + rdev_for_each_rcu(rdev2, mddev2) if (rdev->bdev->bd_contains == - rdev2->bdev->bd_contains) + rdev2->bdev->bd_contains) { + rcu_read_unlock(); return 1; - + } + rcu_read_unlock(); return 0; } @@ -1470,7 +1472,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) kobject_del(&rdev->kobj); goto fail; } - list_add(&rdev->same_set, &mddev->disks); + list_add_rcu(&rdev->same_set, &mddev->disks); bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk); return 0; @@ -1495,14 +1497,16 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev) return; } bd_release_from_disk(rdev->bdev, rdev->mddev->gendisk); - list_del_init(&rdev->same_set); + list_del_rcu(&rdev->same_set); printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b)); rdev->mddev = NULL; sysfs_remove_link(&rdev->kobj, "block"); /* We need to delay this, otherwise we can deadlock when - * writing to 'remove' to "dev/state" + * writing to 'remove' to "dev/state". We also need + * to delay it due to rcu usage. */ + synchronize_rcu(); INIT_WORK(&rdev->del_work, md_delayed_delete); kobject_get(&rdev->kobj); schedule_work(&rdev->del_work); @@ -1558,7 +1562,6 @@ static void export_rdev(mdk_rdev_t * rdev) if (rdev->mddev) MD_BUG(); free_disk_sb(rdev); - list_del_init(&rdev->same_set); #ifndef MODULE if (test_bit(AutoDetected, &rdev->flags)) md_autodetect_dev(rdev->bdev->bd_dev); @@ -4062,8 +4065,10 @@ static void autorun_devices(int part) /* on success, candidates will be empty, on error * it won't... */ - rdev_for_each_list(rdev, tmp, candidates) + rdev_for_each_list(rdev, tmp, candidates) { + list_del_init(&rdev->same_set); export_rdev(rdev); + } mddev_put(mddev); } printk(KERN_INFO "md: ... autorun DONE.\n"); @@ -5529,12 +5534,12 @@ int unregister_md_personality(struct mdk_personality *p) static int is_mddev_idle(mddev_t *mddev) { mdk_rdev_t * rdev; - struct list_head *tmp; int idle; long curr_events; idle = 1; - rdev_for_each(rdev, tmp, mddev) { + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev) { struct gendisk *disk = rdev->bdev->bd_contains->bd_disk; curr_events = disk_stat_read(disk, sectors[0]) + disk_stat_read(disk, sectors[1]) - @@ -5566,6 +5571,7 @@ static int is_mddev_idle(mddev_t *mddev) idle = 0; } } + rcu_read_unlock(); return idle; } diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 4bef4791d80d..9f2549ac0e2d 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -339,6 +339,9 @@ static inline char * mdname (mddev_t * mddev) #define rdev_for_each(rdev, tmp, mddev) \ rdev_for_each_list(rdev, tmp, (mddev)->disks) +#define rdev_for_each_rcu(rdev, mddev) \ + list_for_each_entry_rcu(rdev, &((mddev)->disks), same_set) + typedef struct mdk_thread_s { void (*run) (mddev_t *mddev); mddev_t *mddev; -- cgit From f6fccb1213ba3d661baeb2a5eee0a9701dc03e1b Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Mon, 21 Jul 2008 12:00:37 +0100 Subject: dm: introduce merge_bvec_fn Introduce a bvec merge function for device mapper devices for dynamic size restrictions. This code ensures the requested biovec lies within a single target and then calls a target-specific function to check against any constraints imposed by underlying devices. Signed-off-by: Milan Broz Signed-off-by: Alasdair G Kergon --- drivers/md/dm.c | 44 +++++++++++++++++++++++++++++++++++++++++++ include/linux/device-mapper.h | 6 ++++++ include/linux/dm-ioctl.h | 4 ++-- 3 files changed, 52 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/dm.c b/drivers/md/dm.c index efe969074928..bca448e11878 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -829,6 +829,49 @@ static int __split_bio(struct mapped_device *md, struct bio *bio) * CRUD END *---------------------------------------------------------------*/ +static int dm_merge_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) +{ + struct mapped_device *md = q->queuedata; + struct dm_table *map = dm_get_table(md); + struct dm_target *ti; + sector_t max_sectors; + int max_size; + + if (unlikely(!map)) + return 0; + + ti = dm_table_find_target(map, bvm->bi_sector); + + /* + * Find maximum amount of I/O that won't need splitting + */ + max_sectors = min(max_io_len(md, bvm->bi_sector, ti), + (sector_t) BIO_MAX_SECTORS); + max_size = (max_sectors << SECTOR_SHIFT) - bvm->bi_size; + if (max_size < 0) + max_size = 0; + + /* + * merge_bvec_fn() returns number of bytes + * it can accept at this offset + * max is precomputed maximal io size + */ + if (max_size && ti->type->merge) + max_size = ti->type->merge(ti, bvm, biovec, max_size); + + /* + * Always allow an entire first page + */ + if (max_size <= biovec->bv_len && !(bvm->bi_size >> SECTOR_SHIFT)) + max_size = biovec->bv_len; + + dm_table_put(map); + + return max_size; +} + /* * The request function that just remaps the bio built up by * dm_merge_bvec. @@ -1032,6 +1075,7 @@ static struct mapped_device *alloc_dev(int minor) blk_queue_make_request(md->queue, dm_request); blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY); md->queue->unplug_fn = dm_unplug_all; + blk_queue_merge_bvec(md->queue, dm_merge_bvec); md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache); if (!md->io_pool) diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 0d8d419d191a..a90222e3297d 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -9,11 +9,13 @@ #define _LINUX_DEVICE_MAPPER_H #include +#include struct dm_target; struct dm_table; struct dm_dev; struct mapped_device; +struct bio_vec; typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t; @@ -72,6 +74,9 @@ typedef int (*dm_ioctl_fn) (struct dm_target *ti, struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +typedef int (*dm_merge_fn) (struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size); + void dm_error(const char *message); /* @@ -107,6 +112,7 @@ struct target_type { dm_status_fn status; dm_message_fn message; dm_ioctl_fn ioctl; + dm_merge_fn merge; }; struct io_restrictions { diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h index b03c41bbfa14..28c2940eb30d 100644 --- a/include/linux/dm-ioctl.h +++ b/include/linux/dm-ioctl.h @@ -256,9 +256,9 @@ enum { #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 13 +#define DM_VERSION_MINOR 14 #define DM_VERSION_PATCHLEVEL 0 -#define DM_VERSION_EXTRA "-ioctl (2007-10-18)" +#define DM_VERSION_EXTRA "-ioctl (2008-04-23)" /* Status bits */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */ -- cgit From 584015727a3b88b46602b20077b46cd04f8b4ab3 Mon Sep 17 00:00:00 2001 From: Krzysztof Piotr Oledzki Date: Mon, 21 Jul 2008 10:01:34 -0700 Subject: netfilter: accounting rework: ct_extend + 64bit counters (v4) Initially netfilter has had 64bit counters for conntrack-based accounting, but it was changed in 2.6.14 to save memory. Unfortunately in-kernel 64bit counters are still required, for example for "connbytes" extension. However, 64bit counters waste a lot of memory and it was not possible to enable/disable it runtime. This patch: - reimplements accounting with respect to the extension infrastructure, - makes one global version of seq_print_acct() instead of two seq_print_counters(), - makes it possible to enable it at boot time (for CONFIG_SYSCTL/CONFIG_SYSFS=n), - makes it possible to enable/disable it at runtime by sysctl or sysfs, - extends counters from 32bit to 64bit, - renames ip_conntrack_counter -> nf_conn_counter, - enables accounting code unconditionally (no longer depends on CONFIG_NF_CT_ACCT), - set initial accounting enable state based on CONFIG_NF_CT_ACCT - removes buggy IPCT_COUNTER_FILLING event handling. If accounting is enabled newly created connections get additional acct extend. Old connections are not changed as it is not possible to add a ct_extend area to confirmed conntrack. Accounting is performed for all connections with acct extend regardless of a current state of "net.netfilter.nf_conntrack_acct". Signed-off-by: Krzysztof Piotr Oledzki Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- Documentation/feature-removal-schedule.txt | 10 ++ Documentation/kernel-parameters.txt | 7 ++ include/linux/netfilter/nf_conntrack_common.h | 8 +- include/linux/netfilter/nfnetlink_conntrack.h | 8 +- include/net/netfilter/nf_conntrack.h | 6 -- include/net/netfilter/nf_conntrack_acct.h | 51 ++++++++++ include/net/netfilter/nf_conntrack_extend.h | 2 + .../netfilter/nf_conntrack_l3proto_ipv4_compat.c | 18 +--- net/netfilter/Kconfig | 9 ++ net/netfilter/Makefile | 2 +- net/netfilter/nf_conntrack_acct.c | 104 +++++++++++++++++++++ net/netfilter/nf_conntrack_core.c | 39 +++++--- net/netfilter/nf_conntrack_netlink.c | 44 +++++---- net/netfilter/nf_conntrack_standalone.c | 18 +--- net/netfilter/xt_connbytes.c | 8 +- 15 files changed, 248 insertions(+), 86 deletions(-) create mode 100644 include/net/netfilter/nf_conntrack_acct.h create mode 100644 net/netfilter/nf_conntrack_acct.c (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 86334b6f8238..9f73587219e8 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -336,3 +336,13 @@ When: After the only user (hal) has seen a release with the patches Why: Over 1K .text/.data size reduction, data is available in other ways (ioctls) Who: Johannes Berg + +--------------------------- + +What: CONFIG_NF_CT_ACCT +When: 2.6.29 +Why: Accounting can now be enabled/disabled without kernel recompilation. + Currently used only to set a default value for a feature that is also + controlled by a kernel/module/sysfs/sysctl parameter. +Who: Krzysztof Piotr Oledzki + diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 09ad7450647b..e4ef27584408 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1279,6 +1279,13 @@ and is between 256 and 4096 characters. It is defined in the file This usage is only documented in each driver source file if at all. + nf_conntrack.acct= + [NETFILTER] Enable connection tracking flow accounting + 0 to disable accounting + 1 to enable accounting + Default value depends on CONFIG_NF_CT_ACCT that is + going to be removed in 2.6.29. + nfsaddrs= [NFS] See Documentation/filesystems/nfsroot.txt. diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index bad1eb760f61..885cbe282260 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -122,7 +122,7 @@ enum ip_conntrack_events IPCT_NATINFO_BIT = 10, IPCT_NATINFO = (1 << IPCT_NATINFO_BIT), - /* Counter highest bit has been set */ + /* Counter highest bit has been set, unused */ IPCT_COUNTER_FILLING_BIT = 11, IPCT_COUNTER_FILLING = (1 << IPCT_COUNTER_FILLING_BIT), @@ -145,12 +145,6 @@ enum ip_conntrack_expect_events { }; #ifdef __KERNEL__ -struct ip_conntrack_counter -{ - u_int32_t packets; - u_int32_t bytes; -}; - struct ip_conntrack_stat { unsigned int searched; diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 759bc043dc65..c19595c89304 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -115,10 +115,10 @@ enum ctattr_protoinfo_sctp { enum ctattr_counters { CTA_COUNTERS_UNSPEC, - CTA_COUNTERS_PACKETS, /* old 64bit counters */ - CTA_COUNTERS_BYTES, /* old 64bit counters */ - CTA_COUNTERS32_PACKETS, - CTA_COUNTERS32_BYTES, + CTA_COUNTERS_PACKETS, /* 64bit counters */ + CTA_COUNTERS_BYTES, /* 64bit counters */ + CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */ + CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */ __CTA_COUNTERS_MAX }; #define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 8f5b75734dd0..0741ad592da0 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -88,7 +88,6 @@ struct nf_conn_help { u8 expecting[NF_CT_MAX_EXPECT_CLASSES]; }; - #include #include @@ -111,11 +110,6 @@ struct nf_conn /* Timer function; drops refcnt when it goes off. */ struct timer_list timeout; -#ifdef CONFIG_NF_CT_ACCT - /* Accounting Information (same cache line as other written members) */ - struct ip_conntrack_counter counters[IP_CT_DIR_MAX]; -#endif - #if defined(CONFIG_NF_CONNTRACK_MARK) u_int32_t mark; #endif diff --git a/include/net/netfilter/nf_conntrack_acct.h b/include/net/netfilter/nf_conntrack_acct.h new file mode 100644 index 000000000000..5d5ae55d54c4 --- /dev/null +++ b/include/net/netfilter/nf_conntrack_acct.h @@ -0,0 +1,51 @@ +/* + * (C) 2008 Krzysztof Piotr Oledzki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _NF_CONNTRACK_ACCT_H +#define _NF_CONNTRACK_ACCT_H +#include +#include +#include +#include + +struct nf_conn_counter { + u_int64_t packets; + u_int64_t bytes; +}; + +extern int nf_ct_acct; + +static inline +struct nf_conn_counter *nf_conn_acct_find(const struct nf_conn *ct) +{ + return nf_ct_ext_find(ct, NF_CT_EXT_ACCT); +} + +static inline +struct nf_conn_counter *nf_ct_acct_ext_add(struct nf_conn *ct, gfp_t gfp) +{ + struct nf_conn_counter *acct; + + if (!nf_ct_acct) + return NULL; + + acct = nf_ct_ext_add(ct, NF_CT_EXT_ACCT, gfp); + if (!acct) + pr_debug("failed to add accounting extension area"); + + + return acct; +}; + +extern unsigned int +seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir); + +extern int nf_conntrack_acct_init(void); +extern void nf_conntrack_acct_fini(void); + +#endif /* _NF_CONNTRACK_ACCT_H */ diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index f80c0ed6d870..da8ee52613a5 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -7,11 +7,13 @@ enum nf_ct_ext_id { NF_CT_EXT_HELPER, NF_CT_EXT_NAT, + NF_CT_EXT_ACCT, NF_CT_EXT_NUM, }; #define NF_CT_EXT_HELPER_TYPE struct nf_conn_help #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat +#define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 40a46d482490..3a020720e40b 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c @@ -18,19 +18,7 @@ #include #include #include - -#ifdef CONFIG_NF_CT_ACCT -static unsigned int -seq_print_counters(struct seq_file *s, - const struct ip_conntrack_counter *counter) -{ - return seq_printf(s, "packets=%llu bytes=%llu ", - (unsigned long long)counter->packets, - (unsigned long long)counter->bytes); -} -#else -#define seq_print_counters(x, y) 0 -#endif +#include struct ct_iter_state { unsigned int bucket; @@ -127,7 +115,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_ORIGINAL])) + if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) return -ENOSPC; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) @@ -138,7 +126,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_REPLY])) + if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) return -ENOSPC; if (test_bit(IPS_ASSURED_BIT, &ct->status)) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 316c7af1d2b1..ee898e74808d 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -49,6 +49,15 @@ config NF_CT_ACCT Those counters can be used for flow-based accounting or the `connbytes' match. + Please note that currently this option only sets a default state. + You may change it at boot time with nf_conntrack.acct=0/1 kernel + paramater or by loading the nf_conntrack module with acct=0/1. + + You may also disable/enable it on a running system with: + sysctl net.netfilter.nf_conntrack_acct=0/1 + + This option will be removed in 2.6.29. + If unsure, say `N'. config NF_CONNTRACK_MARK diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 5c4b183f6422..3bd2cc556aea 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -1,6 +1,6 @@ netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o -nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o +nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o obj-$(CONFIG_NETFILTER) = netfilter.o diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c new file mode 100644 index 000000000000..59bd8b903a19 --- /dev/null +++ b/net/netfilter/nf_conntrack_acct.c @@ -0,0 +1,104 @@ +/* Accouting handling for netfilter. */ + +/* + * (C) 2008 Krzysztof Piotr Oledzki + * + * 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 +#include +#include + +#include +#include +#include + +#ifdef CONFIG_NF_CT_ACCT +#define NF_CT_ACCT_DEFAULT 1 +#else +#define NF_CT_ACCT_DEFAULT 0 +#endif + +int nf_ct_acct __read_mostly = NF_CT_ACCT_DEFAULT; +EXPORT_SYMBOL_GPL(nf_ct_acct); + +module_param_named(acct, nf_ct_acct, bool, 0644); +MODULE_PARM_DESC(acct, "Enable connection tracking flow accounting."); + +#ifdef CONFIG_SYSCTL +static struct ctl_table_header *acct_sysctl_header; +static struct ctl_table acct_sysctl_table[] = { + { + .ctl_name = CTL_UNNUMBERED, + .procname = "nf_conntrack_acct", + .data = &nf_ct_acct, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + {} +}; +#endif /* CONFIG_SYSCTL */ + +unsigned int +seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir) +{ + struct nf_conn_counter *acct; + + acct = nf_conn_acct_find(ct); + if (!acct) + return 0; + + return seq_printf(s, "packets=%llu bytes=%llu ", + (unsigned long long)acct[dir].packets, + (unsigned long long)acct[dir].bytes); +}; +EXPORT_SYMBOL_GPL(seq_print_acct); + +static struct nf_ct_ext_type acct_extend __read_mostly = { + .len = sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]), + .align = __alignof__(struct nf_conn_counter[IP_CT_DIR_MAX]), + .id = NF_CT_EXT_ACCT, +}; + +int nf_conntrack_acct_init(void) +{ + int ret; + +#ifdef CONFIG_NF_CT_ACCT + printk(KERN_WARNING "CONFIG_NF_CT_ACCT is deprecated and will be removed soon. Plase use\n"); + printk(KERN_WARNING "nf_conntrack.acct=1 kernel paramater, acct=1 nf_conntrack module option or\n"); + printk(KERN_WARNING "sysctl net.netfilter.nf_conntrack_acct=1 to enable it.\n"); +#endif + + ret = nf_ct_extend_register(&acct_extend); + if (ret < 0) { + printk(KERN_ERR "nf_conntrack_acct: Unable to register extension\n"); + return ret; + } + +#ifdef CONFIG_SYSCTL + acct_sysctl_header = register_sysctl_paths(nf_net_netfilter_sysctl_path, + acct_sysctl_table); + + if (!acct_sysctl_header) { + nf_ct_extend_unregister(&acct_extend); + + printk(KERN_ERR "nf_conntrack_acct: can't register to sysctl.\n"); + return -ENOMEM; + } +#endif + + return 0; +} + +void nf_conntrack_acct_fini(void) +{ +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(acct_sysctl_header); +#endif + nf_ct_extend_unregister(&acct_extend); +} diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 28d03e64200b..c519d090bdb9 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -37,6 +37,7 @@ #include #include #include +#include #define NF_CONNTRACK_VERSION "0.5.0" @@ -555,6 +556,8 @@ init_conntrack(const struct nf_conntrack_tuple *tuple, return NULL; } + nf_ct_acct_ext_add(ct, GFP_ATOMIC); + spin_lock_bh(&nf_conntrack_lock); exp = nf_ct_find_expectation(tuple); if (exp) { @@ -828,17 +831,16 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, } acct: -#ifdef CONFIG_NF_CT_ACCT if (do_acct) { - ct->counters[CTINFO2DIR(ctinfo)].packets++; - ct->counters[CTINFO2DIR(ctinfo)].bytes += - skb->len - skb_network_offset(skb); + struct nf_conn_counter *acct; - if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000) - || (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000)) - event |= IPCT_COUNTER_FILLING; + acct = nf_conn_acct_find(ct); + if (acct) { + acct[CTINFO2DIR(ctinfo)].packets++; + acct[CTINFO2DIR(ctinfo)].bytes += + skb->len - skb_network_offset(skb); + } } -#endif spin_unlock_bh(&nf_conntrack_lock); @@ -853,15 +855,19 @@ bool __nf_ct_kill_acct(struct nf_conn *ct, const struct sk_buff *skb, int do_acct) { -#ifdef CONFIG_NF_CT_ACCT if (do_acct) { + struct nf_conn_counter *acct; + spin_lock_bh(&nf_conntrack_lock); - ct->counters[CTINFO2DIR(ctinfo)].packets++; - ct->counters[CTINFO2DIR(ctinfo)].bytes += - skb->len - skb_network_offset(skb); + acct = nf_conn_acct_find(ct); + if (acct) { + acct[CTINFO2DIR(ctinfo)].packets++; + acct[CTINFO2DIR(ctinfo)].bytes += + skb->len - skb_network_offset(skb); + } spin_unlock_bh(&nf_conntrack_lock); } -#endif + if (del_timer(&ct->timeout)) { ct->timeout.function((unsigned long)ct); return true; @@ -1029,6 +1035,7 @@ void nf_conntrack_cleanup(void) nf_conntrack_proto_fini(); nf_conntrack_helper_fini(); nf_conntrack_expect_fini(); + nf_conntrack_acct_fini(); } struct hlist_head *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced) @@ -1168,6 +1175,10 @@ int __init nf_conntrack_init(void) if (ret < 0) goto out_fini_expect; + ret = nf_conntrack_acct_init(); + if (ret < 0) + goto out_fini_helper; + /* For use by REJECT target */ rcu_assign_pointer(ip_ct_attach, nf_conntrack_attach); rcu_assign_pointer(nf_ct_destroy, destroy_conntrack); @@ -1180,6 +1191,8 @@ int __init nf_conntrack_init(void) return ret; +out_fini_helper: + nf_conntrack_helper_fini(); out_fini_expect: nf_conntrack_expect_fini(); out_fini_proto: diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 95a7967731f9..105a616c5c78 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef CONFIG_NF_NAT_NEEDED #include #include @@ -206,22 +207,26 @@ nla_put_failure: return -1; } -#ifdef CONFIG_NF_CT_ACCT static int ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct, enum ip_conntrack_dir dir) { enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; struct nlattr *nest_count; + const struct nf_conn_counter *acct; + + acct = nf_conn_acct_find(ct); + if (!acct) + return 0; nest_count = nla_nest_start(skb, type | NLA_F_NESTED); if (!nest_count) goto nla_put_failure; - NLA_PUT_BE32(skb, CTA_COUNTERS32_PACKETS, - htonl(ct->counters[dir].packets)); - NLA_PUT_BE32(skb, CTA_COUNTERS32_BYTES, - htonl(ct->counters[dir].bytes)); + NLA_PUT_BE64(skb, CTA_COUNTERS_PACKETS, + cpu_to_be64(acct[dir].packets)); + NLA_PUT_BE64(skb, CTA_COUNTERS_BYTES, + cpu_to_be64(acct[dir].bytes)); nla_nest_end(skb, nest_count); @@ -230,9 +235,6 @@ ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct, nla_put_failure: return -1; } -#else -#define ctnetlink_dump_counters(a, b, c) (0) -#endif #ifdef CONFIG_NF_CONNTRACK_MARK static inline int @@ -501,11 +503,6 @@ static int ctnetlink_conntrack_event(struct notifier_block *this, goto nla_put_failure; #endif - if (events & IPCT_COUNTER_FILLING && - (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)) - goto nla_put_failure; - if (events & IPCT_RELATED && ctnetlink_dump_master(skb, ct) < 0) goto nla_put_failure; @@ -576,11 +573,15 @@ restart: cb->args[1] = (unsigned long)ct; goto out; } -#ifdef CONFIG_NF_CT_ACCT + if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == - IPCTNL_MSG_CT_GET_CTRZERO) - memset(&ct->counters, 0, sizeof(ct->counters)); -#endif + IPCTNL_MSG_CT_GET_CTRZERO) { + struct nf_conn_counter *acct; + + acct = nf_conn_acct_find(ct); + if (acct) + memset(acct, 0, sizeof(struct nf_conn_counter[IP_CT_DIR_MAX])); + } } if (cb->args[1]) { cb->args[1] = 0; @@ -832,14 +833,9 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, u_int8_t u3 = nfmsg->nfgen_family; int err = 0; - if (nlh->nlmsg_flags & NLM_F_DUMP) { -#ifndef CONFIG_NF_CT_ACCT - if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO) - return -ENOTSUPP; -#endif + if (nlh->nlmsg_flags & NLM_F_DUMP) return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table, ctnetlink_done); - } if (cda[CTA_TUPLE_ORIG]) err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG, u3); @@ -1152,6 +1148,8 @@ ctnetlink_create_conntrack(struct nlattr *cda[], goto err; } + nf_ct_acct_ext_add(ct, GFP_KERNEL); + #if defined(CONFIG_NF_CONNTRACK_MARK) if (cda[CTA_MARK]) ct->mark = ntohl(nla_get_be32(cda[CTA_MARK])); diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 46ea542d0df9..869ef9349d0f 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -25,6 +25,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); @@ -38,19 +39,6 @@ print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, } EXPORT_SYMBOL_GPL(print_tuple); -#ifdef CONFIG_NF_CT_ACCT -static unsigned int -seq_print_counters(struct seq_file *s, - const struct ip_conntrack_counter *counter) -{ - return seq_printf(s, "packets=%llu bytes=%llu ", - (unsigned long long)counter->packets, - (unsigned long long)counter->bytes); -} -#else -#define seq_print_counters(x, y) 0 -#endif - struct ct_iter_state { unsigned int bucket; }; @@ -146,7 +134,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_ORIGINAL])) + if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) return -ENOSPC; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) @@ -157,7 +145,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_REPLY])) + if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) return -ENOSPC; if (test_bit(IPS_ASSURED_BIT, &ct->status)) diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index d7e8983cd37f..3e39c4fe1931 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -8,6 +8,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte "); @@ -27,12 +28,15 @@ connbytes_mt(const struct sk_buff *skb, const struct net_device *in, u_int64_t what = 0; /* initialize to make gcc happy */ u_int64_t bytes = 0; u_int64_t pkts = 0; - const struct ip_conntrack_counter *counters; + const struct nf_conn_counter *counters; ct = nf_ct_get(skb, &ctinfo); if (!ct) return false; - counters = ct->counters; + + counters = nf_conn_acct_find(ct); + if (!counters) + return false; switch (sinfo->what) { case XT_CONNBYTES_PKTS: -- cgit From 72961ecf84d67d6359a1b30f9b2a8427f13e1e71 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Mon, 21 Jul 2008 10:02:35 -0700 Subject: netfilter: nfnetlink_log: send complete hardware header This patch adds some fields to NFLOG to be able to send the complete hardware header with all necessary informations. It sends to userspace: * the type of hardware link * the lenght of hardware header * the hardware header Signed-off-by: Eric Leblond Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter/nfnetlink_log.h | 3 +++ net/netfilter/nfnetlink_log.c | 8 ++++++++ 2 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter/nfnetlink_log.h b/include/linux/netfilter/nfnetlink_log.h index a85721332924..f661731f3cb1 100644 --- a/include/linux/netfilter/nfnetlink_log.h +++ b/include/linux/netfilter/nfnetlink_log.h @@ -48,6 +48,9 @@ enum nfulnl_attr_type { NFULA_SEQ, /* instance-local sequence number */ NFULA_SEQ_GLOBAL, /* global sequence number */ NFULA_GID, /* group id of socket */ + NFULA_HWTYPE, /* hardware type */ + NFULA_HWHEADER, /* hardware header */ + NFULA_HWLEN, /* hardware header length */ __NFULA_MAX }; diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index b8173af8c24a..9a35b57ab76d 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -453,6 +453,14 @@ __build_packet_message(struct nfulnl_instance *inst, } } + if (indev && skb_mac_header_was_set(skb)) { + NLA_PUT_BE16(inst->skb, NFULA_HWTYPE, htons(skb->dev->type)); + NLA_PUT_BE16(inst->skb, NFULA_HWLEN, + htons(skb->dev->hard_header_len)); + NLA_PUT(inst->skb, NFULA_HWHEADER, skb->dev->hard_header_len, + skb_mac_header(skb)); + } + if (skb->tstamp.tv64) { struct nfulnl_msg_packet_timestamp ts; struct timeval tv = ktime_to_timeval(skb->tstamp); -- cgit From 6579e57b31d79d31d9b806e41ba48774e73257dc Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Mon, 21 Jul 2008 13:31:48 -0700 Subject: net: Print the module name as part of the watchdog message As suggested by Dave: This patch adds a function to get the driver name from a struct net_device, and consequently uses this in the watchdog timeout handler to print as part of the message. Signed-off-by: Arjan van de Ven Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 20 ++++++++++++++++++++ net/sched/sch_generic.c | 6 +++--- 3 files changed, 25 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 812bcd8b4363..f5ea445f89f0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1645,6 +1645,8 @@ extern void dev_seq_stop(struct seq_file *seq, void *v); extern int netdev_class_create_file(struct class_attribute *class_attr); extern void netdev_class_remove_file(struct class_attribute *class_attr); +extern char *netdev_drivername(struct net_device *dev, char *buffer, int len); + extern void linkwatch_run_queue(void); extern int netdev_compute_features(unsigned long all, unsigned long one); diff --git a/net/core/dev.c b/net/core/dev.c index 1698b3998981..ad5598d2bb37 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4686,6 +4686,26 @@ err_name: return -ENOMEM; } +char *netdev_drivername(struct net_device *dev, char *buffer, int len) +{ + struct device_driver *driver; + struct device *parent; + + if (len <= 0 || !buffer) + return buffer; + buffer[0] = 0; + + parent = dev->dev.parent; + + if (!parent) + return buffer; + + driver = parent->driver; + if (driver && driver->name) + strlcpy(buffer, driver->name, len); + return buffer; +} + static void __net_exit netdev_exit(struct net *net) { kfree(net->dev_name_head); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index cb625b4d6da5..4ac7e3a8c253 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -212,9 +212,9 @@ static void dev_watchdog(unsigned long arg) if (some_queue_stopped && time_after(jiffies, (dev->trans_start + dev->watchdog_timeo))) { - printk(KERN_INFO "NETDEV WATCHDOG: %s: " - "transmit timed out\n", - dev->name); + char drivername[64]; + printk(KERN_INFO "NETDEV WATCHDOG: %s (%s): transmit timed out\n", + dev->name, netdev_drivername(dev, drivername, 64)); dev->tx_timeout(dev); WARN_ON_ONCE(1); } -- cgit From ebb36a978131810c98e7198b1187090c697cf99f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 21 Jul 2008 13:41:16 -0700 Subject: ipv6: __KERNEL__ ifdef struct ipv6_devconf Based upon a report by Olaf Hering. Signed-off-by: David S. Miller --- include/linux/ipv6.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 391ad0843a46..641e026eee8f 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -123,6 +123,7 @@ struct ipv6hdr { struct in6_addr daddr; }; +#ifdef __KERNEL__ /* * This structure contains configuration options per IPv6 link. */ @@ -167,6 +168,7 @@ struct ipv6_devconf { __s32 accept_dad; void *sysctl; }; +#endif /* index values for the variables in ipv6_devconf */ enum { -- cgit From 1b26da1510c02a2dac33c0ea48904256dcec4617 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 2 Jul 2008 12:46:22 -0700 Subject: USB: handle pci_name() being const This changes usb_create_hcd() to be able to handle the fact that pci_name() has changed to a constant string. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 +- drivers/usb/core/hcd.h | 2 +- include/linux/usb.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 42a436478b78..07f7bedf4dca 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1764,7 +1764,7 @@ EXPORT_SYMBOL_GPL (usb_hc_died); * If memory is unavailable, returns NULL. */ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, - struct device *dev, char *bus_name) + struct device *dev, const char *bus_name) { struct usb_hcd *hcd; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index b9de1569b39e..50b8bb2d1212 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -235,7 +235,7 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev, extern int usb_hcd_get_frame_number(struct usb_device *udev); extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, - struct device *dev, char *bus_name); + struct device *dev, const char *bus_name); extern struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd); extern void usb_put_hcd(struct usb_hcd *hcd); extern int usb_add_hcd(struct usb_hcd *hcd, diff --git a/include/linux/usb.h b/include/linux/usb.h index c08689ea9b4b..cee7fbb2b605 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -293,7 +293,7 @@ struct usb_devmap { struct usb_bus { struct device *controller; /* host/master side hardware */ int busnum; /* Bus number (in order of reg) */ - char *bus_name; /* stable id (PCI slot_name etc) */ + const char *bus_name; /* stable id (PCI slot_name etc) */ u8 uses_dma; /* Does the host controller use DMA? */ u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ -- cgit From 9da82bd4649334817ef0e752a69eb99051645dad Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 8 May 2008 11:54:37 -0400 Subject: USB: implement "soft" unbinding This patch (as1091) changes the way usbcore handles interface unbinding. If the interface's driver supports "soft" unbinding (a new flag in the driver structure) then in-flight URBs are not cancelled and endpoints are not disabled. Instead the driver is allowed to continue communicating with the device (although of course it should stop before its disconnect routine returns). The purpose of this change is to allow drivers to do a clean shutdown when they get unbound from a device that is still plugged in. Killing all the URBs and disabling the endpoints before calling the driver's disconnect method doesn't give the driver any control over what happens, and it can leave devices in indeterminate states. For example, when usb-storage unbinds it doesn't want to stop while in the middle of transmitting a SCSI command. The soft_unbind flag is added because in the past, a number of drivers have experienced problems related to ongoing I/O after their disconnect routine returned. Hence "soft" unbinding is made available only to drivers that claim to support it. The patch also replaces "interface_to_usbdev(intf)" with "udev" in a couple of places, a minor simplification. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 11 ++++++----- include/linux/usb.h | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0a0e8cea0afc..8da1a56659be 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -257,15 +257,16 @@ static int usb_unbind_interface(struct device *dev) udev = interface_to_usbdev(intf); error = usb_autoresume_device(udev); - /* release all urbs for this interface */ - usb_disable_interface(interface_to_usbdev(intf), intf); + /* Terminate all URBs for this interface unless the driver + * supports "soft" unbinding. + */ + if (!driver->soft_unbind) + usb_disable_interface(udev, intf); driver->disconnect(intf); /* reset other interface state */ - usb_set_interface(interface_to_usbdev(intf), - intf->altsetting[0].desc.bInterfaceNumber, - 0); + usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); usb_set_intfdata(intf, NULL); intf->condition = USB_INTERFACE_UNBOUND; diff --git a/include/linux/usb.h b/include/linux/usb.h index cee7fbb2b605..8429d08bd2fd 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -972,6 +972,8 @@ struct usbdrv_wrap { * added to this driver by preventing the sysfs file from being created. * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend * for interfaces bound to this driver. + * @soft_unbind: if set to 1, the USB core will not kill URBs and disable + * endpoints before calling the driver's disconnect method. * * USB interface drivers must provide a name, probe() and disconnect() * methods, and an id_table. Other driver fields are optional. @@ -1012,6 +1014,7 @@ struct usb_driver { struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1; unsigned int supports_autosuspend:1; + unsigned int soft_unbind:1; }; #define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver) -- cgit From ea05af61a874ffbc158d9cf06df8a9396f299f38 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 01:00:46 +0300 Subject: USB: remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devices.c | 2 -- drivers/usb/core/devio.c | 2 -- drivers/usb/gadget/rndis.c | 2 -- drivers/usb/gadget/rndis.h | 2 -- drivers/usb/misc/emi62.c | 2 -- drivers/usb/serial/digi_acceleport.c | 2 -- drivers/usb/storage/datafab.c | 2 -- drivers/usb/storage/debug.c | 2 -- drivers/usb/storage/debug.h | 2 -- drivers/usb/storage/dpcm.c | 2 -- drivers/usb/storage/dpcm.h | 2 -- drivers/usb/storage/freecom.c | 2 -- drivers/usb/storage/freecom.h | 2 -- drivers/usb/storage/initializers.c | 2 -- drivers/usb/storage/initializers.h | 2 -- drivers/usb/storage/isd200.c | 2 -- drivers/usb/storage/jumpshot.c | 2 -- drivers/usb/storage/protocol.c | 2 -- drivers/usb/storage/protocol.h | 2 -- drivers/usb/storage/scsiglue.c | 2 -- drivers/usb/storage/scsiglue.h | 2 -- drivers/usb/storage/sddr09.c | 1 - drivers/usb/storage/sddr09.h | 2 -- drivers/usb/storage/sddr55.c | 2 -- drivers/usb/storage/sddr55.h | 2 -- drivers/usb/storage/shuttle_usbat.c | 2 -- drivers/usb/storage/shuttle_usbat.h | 2 -- drivers/usb/storage/transport.c | 2 -- drivers/usb/storage/transport.h | 2 -- drivers/usb/storage/unusual_devs.h | 2 -- drivers/usb/storage/usb.c | 2 -- drivers/usb/storage/usb.h | 2 -- include/linux/usbdevice_fs.h | 2 -- 33 files changed, 65 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 83d9dc379d96..a681d9b92bda 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -46,8 +46,6 @@ * 2000-07-05: Ashley Montanaro * Converted file reading routine to dump to buffer once * per device, not per bus - * - * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $ */ #include diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 9218cca21043..7bee9c18b3bc 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -19,8 +19,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ - * * This file implements the usbfs/x/y files, where * x is the bus number and y the device number. * diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index d0677f5d3cd5..20b15fcc9d49 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1,8 +1,6 @@ /* * RNDIS MSG parser * - * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ - * * Authors: Benedikt Spranger, Pengutronix * Robert Schwebel, Pengutronix * diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 397b149f3ca7..b917b4f34ea8 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -1,8 +1,6 @@ /* * RNDIS Definitions for Remote NDIS * - * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $ - * * Authors: Benedikt Spranger, Pengutronix * Robert Schwebel, Pengutronix * diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c index 20886c21e739..5d859ded5bbf 100644 --- a/drivers/usb/misc/emi62.c +++ b/drivers/usb/misc/emi62.c @@ -6,8 +6,6 @@ * 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. - * - * $Id: emi62.c,v 1.15 2002/04/23 06:13:59 tapio Exp $ */ #include #include diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 04a56f300ea6..d96302d0d96e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -229,8 +229,6 @@ * in case a wake up is lost. * - Following Documentation/DocBook/kernel-locking.pdf no spin locks * are held when calling copy_to/from_user or printk. -* -* $Id: digi_acceleport.c,v 1.80.1.2 2000/11/02 05:45:08 root Exp $ */ #include diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 579e9f52053a..17f1ae232919 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -1,6 +1,4 @@ /* Driver for Datafab USB Compact Flash reader - * - * $Id: datafab.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $ * * datafab driver v0.1: * diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index 01e430654a13..a2b5526c9fa0 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Debugging Functions Source Code File * - * $Id: debug.c,v 1.9 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index 77e244a8c376..dbb985d52423 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Debugging Functions Header File * - * $Id: debug.h,v 1.6 2001/01/12 23:51:04 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/dpcm.c b/drivers/usb/storage/dpcm.c index 9a410b5a6e5b..939923471af4 100644 --- a/drivers/usb/storage/dpcm.c +++ b/drivers/usb/storage/dpcm.c @@ -1,6 +1,4 @@ /* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader - * - * $Id: dpcm.c,v 1.4 2001/06/11 02:54:25 mdharm Exp $ * * DPCM driver v0.1: * diff --git a/drivers/usb/storage/dpcm.h b/drivers/usb/storage/dpcm.h index 81b464cfcc1e..e7b7b0f120d7 100644 --- a/drivers/usb/storage/dpcm.h +++ b/drivers/usb/storage/dpcm.h @@ -1,6 +1,4 @@ /* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader - * - * $Id: dpcm.h,v 1.2 2000/08/25 00:13:51 mdharm Exp $ * * DPCM driver v0.1: * diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index f5a4e8d6a3b1..7a4d45677227 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -1,6 +1,4 @@ /* Driver for Freecom USB/IDE adaptor - * - * $Id: freecom.c,v 1.22 2002/04/22 03:39:43 mdharm Exp $ * * Freecom v0.1: * diff --git a/drivers/usb/storage/freecom.h b/drivers/usb/storage/freecom.h index 1b012d62d0a8..20d0fe6ba0c8 100644 --- a/drivers/usb/storage/freecom.h +++ b/drivers/usb/storage/freecom.h @@ -1,6 +1,4 @@ /* Driver for Freecom USB/IDE adaptor - * - * $Id: freecom.h,v 1.4 2000/08/29 14:49:15 dlbrown Exp $ * * Freecom v0.1: * diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 187dd1e01093..4995bb595aef 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -1,6 +1,4 @@ /* Special Initializers for certain USB Mass Storage devices - * - * $Id: initializers.c,v 1.2 2000/09/06 22:35:57 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index ad3ffd4236c2..529327fbb06b 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -1,6 +1,4 @@ /* Header file for Special Initializers for certain USB Mass Storage devices - * - * $Id: initializers.h,v 1.1 2000/08/29 23:07:02 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index a153335f3648..383abf2516a5 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1,6 +1,4 @@ /* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC - * - * $Id: isd200.c,v 1.16 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance: * (C) 2001-2002 Björn Stenberg (bjorn@haxx.se) diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 61097cbb1585..df67f13c9e73 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -1,6 +1,4 @@ /* Driver for Lexar "Jumpshot" Compact Flash reader - * - * $Id: jumpshot.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $ * * jumpshot driver v0.1: * diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index b9b8ede61fb3..3b3357e20ea7 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -1,6 +1,4 @@ /* Driver for USB Mass Storage compliant devices - * - * $Id: protocol.c,v 1.14 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h index 8737a36891ca..487056ffb516 100644 --- a/drivers/usb/storage/protocol.h +++ b/drivers/usb/storage/protocol.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Protocol Functions Header File * - * $Id: protocol.h,v 1.4 2001/02/13 07:10:03 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 043b60b2ad17..b4c9e0f18a82 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * SCSI layer glue code * - * $Id: scsiglue.c,v 1.26 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h index 737e4fa6045f..ffa1cca93d2c 100644 --- a/drivers/usb/storage/scsiglue.h +++ b/drivers/usb/storage/scsiglue.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * SCSI Connecting Glue Header File * - * $Id: scsiglue.h,v 1.4 2000/08/25 00:13:51 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 8972b17da843..c5a54b872c24 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -1,6 +1,5 @@ /* Driver for SanDisk SDDR-09 SmartMedia reader * - * $Id: sddr09.c,v 1.24 2002/04/22 03:39:43 mdharm Exp $ * (c) 2000, 2001 Robert Baruch (autophile@starband.net) * (c) 2002 Andries Brouwer (aeb@cwi.nl) * Developed with the assistance of: diff --git a/drivers/usb/storage/sddr09.h b/drivers/usb/storage/sddr09.h index c03089a9ec38..e50033ad7b19 100644 --- a/drivers/usb/storage/sddr09.h +++ b/drivers/usb/storage/sddr09.h @@ -1,8 +1,6 @@ /* Driver for SanDisk SDDR-09 SmartMedia reader * Header File * - * $Id: sddr09.h,v 1.5 2000/08/25 00:13:51 mdharm Exp $ - * * Current development and maintenance by: * (c) 2000 Robert Baruch (autophile@dol.net) * (c) 2002 Andries Brouwer (aeb@cwi.nl) diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index 6d14327c921d..0d8df7577899 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -1,6 +1,4 @@ /* Driver for SanDisk SDDR-55 SmartMedia reader - * - * $Id:$ * * SDDR55 driver v0.1: * diff --git a/drivers/usb/storage/sddr55.h b/drivers/usb/storage/sddr55.h index d6bd32f6c9f3..a815a0470c84 100644 --- a/drivers/usb/storage/sddr55.h +++ b/drivers/usb/storage/sddr55.h @@ -1,8 +1,6 @@ /* Driver for SanDisk SDDR-55 SmartMedia reader * Header File * - * $Id:$ - * * Current development and maintenance by: * (c) 2002 Simon Munton * diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index 570c1250f6f3..ae6d64810d2a 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1,6 +1,4 @@ /* Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable - * - * $Id: shuttle_usbat.c,v 1.17 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 2000, 2001 Robert Baruch (autophile@starband.net) diff --git a/drivers/usb/storage/shuttle_usbat.h b/drivers/usb/storage/shuttle_usbat.h index 3ddf143a1dec..d8bfc43e9044 100644 --- a/drivers/usb/storage/shuttle_usbat.h +++ b/drivers/usb/storage/shuttle_usbat.h @@ -1,8 +1,6 @@ /* Driver for SCM Microsystems USB-ATAPI cable * Header File * - * $Id: shuttle_usbat.h,v 1.5 2000/09/17 14:44:52 groovyjava Exp $ - * * Current development and maintenance by: * (c) 2000 Robert Baruch (autophile@dol.net) * (c) 2004, 2005 Daniel Drake diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 94138df557b9..08d3a13fec2c 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1,6 +1,4 @@ /* Driver for USB Mass Storage compliant devices - * - * $Id: transport.c,v 1.47 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index ada7c2f43f84..e70b88182f0e 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Transport Functions Header File * - * $Id: transport.h,v 1.18 2002/04/21 02:57:59 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 3ba972c20f3c..7ae69f55aa96 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Unusual Devices File * - * $Id: unusual_devs.h,v 1.32 2002/02/25 02:41:24 mdharm Exp $ - * * Current development and maintenance by: * (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 6bfd99dd57aa..bfea851be985 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,6 +1,4 @@ /* Driver for USB Mass Storage compliant devices - * - * $Id: usb.c,v 1.75 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 1999-2003 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 47906dc620db..a4ad73bd832d 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Main Header File * - * $Id: usb.h,v 1.21 2002/04/21 02:57:59 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 3118ede2c67b..0044d9b4cb85 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -22,8 +22,6 @@ * * History: * 0.1 04.01.2000 Created - * - * $Id: usbdevice_fs.h,v 1.1 2000/01/06 18:40:41 tom Exp $ */ /*****************************************************************************/ -- cgit From a4c39c41bf3592684e36fa0dbbd4ab1a31f969b9 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 19 Jun 2008 17:52:25 -0700 Subject: usb gadget: descriptor copying support Define three new descriptor manipulation utilities, for use when setting up functions that may have multiple instances: usb_copy_descriptors() to copy a vector of descriptors usb_free_descriptors() to free the copy usb_find_endpoint() to find a copied version These will be used as follows. Functions will continue to have static tables of descriptors they update, now used as __initdata templates. When a function creates a new instance, it patches those tables with relevant interface and string IDs, plus endpoint assignments. Then it copies those morphed descriptors, associates the copies with the new function instance, and records the endpoint descriptors to use when activating the endpoints. When initialization is done, only the copies remain in memory. The copies are freed on driver removal. This ensures that each instance has descriptors which hold the right instance-specific data. Two instances in the same configuration will obviously never share the same interface IDs or use the same endpoints. Instances in different configurations won't do so either, which means this is slightly less memory-efficient in some cases. This also includes a bugfix to the epautoconf code that shows up with this usage model. It must replace the previous endpoint number when updating the template descriptors, not just mask in a few more bits. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/config.c | 76 ++++++++++++++++++++++++++++++++++++++++- drivers/usb/gadget/epautoconf.c | 1 + include/linux/usb/gadget.h | 19 +++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index a4e54b2743f0..1ca1c326392a 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -96,7 +96,7 @@ int usb_gadget_config_buf( /* config descriptor first */ if (length < USB_DT_CONFIG_SIZE || !desc) return -EINVAL; - *cp = *config; + *cp = *config; /* then interface/endpoint/class/vendor/... */ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, @@ -115,3 +115,77 @@ int usb_gadget_config_buf( return len; } +/** + * usb_copy_descriptors - copy a vector of USB descriptors + * @src: null-terminated vector to copy + * Context: initialization code, which may sleep + * + * This makes a copy of a vector of USB descriptors. Its primary use + * is to support usb_function objects which can have multiple copies, + * each needing different descriptors. Functions may have static + * tables of descriptors, which are used as templates and customized + * with identifiers (for interfaces, strings, endpoints, and more) + * as needed by a given function instance. + */ +struct usb_descriptor_header **__init +usb_copy_descriptors(struct usb_descriptor_header **src) +{ + struct usb_descriptor_header **tmp; + unsigned bytes; + unsigned n_desc; + void *mem; + struct usb_descriptor_header **ret; + + /* count descriptors and their sizes; then add vector size */ + for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++) + bytes += (*tmp)->bLength; + bytes += (n_desc + 1) * sizeof(*tmp); + + mem = kmalloc(bytes, GFP_KERNEL); + if (!mem) + return NULL; + + /* fill in pointers starting at "tmp", + * to descriptors copied starting at "mem"; + * and return "ret" + */ + tmp = mem; + ret = mem; + mem += (n_desc + 1) * sizeof(*tmp); + while (*src) { + memcpy(mem, *src, (*src)->bLength); + *tmp = mem; + tmp++; + mem += (*src)->bLength; + src++; + } + *tmp = NULL; + + return ret; +} + +/** + * usb_find_endpoint - find a copy of an endpoint descriptor + * @src: original vector of descriptors + * @copy: copy of @src + * @ep: endpoint descriptor found in @src + * + * This returns the copy of the @match descriptor made for @copy. Its + * intended use is to help remembering the endpoint descriptor to use + * when enabling a given endpoint. + */ +struct usb_endpoint_descriptor *__init +usb_find_endpoint( + struct usb_descriptor_header **src, + struct usb_descriptor_header **copy, + struct usb_endpoint_descriptor *match +) +{ + while (*src) { + if (*src == (void *) match) + return (void *)*copy; + src++; + copy++; + } + return NULL; +} diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 8bdad221fa91..9462e30192d8 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -159,6 +159,7 @@ ep_matches ( /* MATCH!! */ /* report address */ + desc->bEndpointAddress &= USB_DIR_IN; if (isdigit (ep->name [2])) { u8 num = simple_strtol (&ep->name [2], NULL, 10); desc->bEndpointAddress |= num; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index cf468fbdbf8e..0ebedaec075d 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -858,6 +858,25 @@ int usb_descriptor_fillbuf(void *, unsigned, int usb_gadget_config_buf(const struct usb_config_descriptor *config, void *buf, unsigned buflen, const struct usb_descriptor_header **desc); +/* copy a NULL-terminated vector of descriptors */ +struct usb_descriptor_header **usb_copy_descriptors( + struct usb_descriptor_header **); + +/* return copy of endpoint descriptor given original descriptor set */ +struct usb_endpoint_descriptor *usb_find_endpoint( + struct usb_descriptor_header **src, + struct usb_descriptor_header **copy, + struct usb_endpoint_descriptor *match); + +/** + * usb_free_descriptors - free descriptors returned by usb_copy_descriptors() + * @v: vector of descriptors + */ +static inline void usb_free_descriptors(struct usb_descriptor_header **v) +{ + kfree(v); +} + /*-------------------------------------------------------------------------*/ /* utility wrapping a simple endpoint selection policy */ -- cgit From 40982be52d8f64c3e10adce17e66ab755a4fa26b Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 19 Jun 2008 17:52:58 -0700 Subject: usb gadget: composite gadget core Add interfaces for composite gadget drivers, and basic implementation support behind it: - struct usb_function ... groups one or more interfaces into a function managed as one unit within a configuration, to which it's added by usb_add_function(). - struct usb_configuration ... groups one or more such functions into a configuration managed as one unit by a driver, to which it's added by usb_add_config(). These operate at either high or full/low speeds and at a given bMaxPower. - struct usb_composite_driver ... groups one or more such configurations into a gadget driver, which may be registered or unregistered. - struct usb_composite_dev ... a usb_composite_driver manages this; it wraps the usb_gadget exposed by the controller driver. This also includes some basic kerneldoc. How to use it (the short version): provide a usb_composite_driver with a bind() that calls usb_add_config() for each of the needed configurations. The configurations in turn have bind() calls, which will usb_add_function() for each function required. Each function's bind() allocates resources needed to perform its tasks, like endpoints; sometimes configurations will allocate resources too. Separate patches will convert most gadget drivers to this infrastructure. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/gadget.tmpl | 35 ++ drivers/usb/gadget/composite.c | 1040 +++++++++++++++++++++++++++++++++++++ include/linux/usb/composite.h | 338 ++++++++++++ 3 files changed, 1413 insertions(+) create mode 100644 drivers/usb/gadget/composite.c create mode 100644 include/linux/usb/composite.h (limited to 'include/linux') diff --git a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl index 5a8ffa761e09..478bfe16a19d 100644 --- a/Documentation/DocBook/gadget.tmpl +++ b/Documentation/DocBook/gadget.tmpl @@ -524,6 +524,41 @@ These utilities include endpoint autoconfiguration. +Composite Device Framework + +The core API is sufficient for writing drivers for composite +USB devices (with more than one function in a given configuration), +and also multi-configuration devices (also more than one function, +but not necessarily sharing a given configuration). +There is however an optional framework which makes it easier to +reuse and combine functions. + + +Devices using this framework provide a struct +usb_composite_driver, which in turn provides one or +more struct usb_configuration instances. +Each such configuration includes at least one +struct usb_function, which packages a user +visible role such as "network link" or "mass storage device". +Management functions may also exist, such as "Device Firmware +Upgrade". + + +!Iinclude/linux/usb/composite.h +!Edrivers/usb/gadget/composite.c + + + +Composite Device Functions + +At this writing, a few of the current gadget drivers have +been converted to this framework. +Near-term plans include converting all of them, except for "gadgetfs". + + + + + Peripheral Controller Drivers diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c new file mode 100644 index 000000000000..b3d302a6a081 --- /dev/null +++ b/drivers/usb/gadget/composite.c @@ -0,0 +1,1040 @@ +/* + * composite.c - infrastructure for Composite USB Gadgets + * + * Copyright (C) 2006-2008 David Brownell + * + * 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 + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include + +#include + + +/* + * The code in this file is utility code, used to build a gadget driver + * from one or more "function" drivers, one or more "configuration" + * objects, and a "usb_composite_driver" by gluing them together along + * with the relevant device-wide data. + */ + +/* big enough to hold our biggest descriptor */ +#define USB_BUFSIZ 512 + +static struct usb_composite_driver *composite; + +/* Some systems will need runtime overrides for the product identifers + * published in the device descriptor, either numbers or strings or both. + * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). + */ + +static ushort idVendor; +module_param(idVendor, ushort, 0); +MODULE_PARM_DESC(idVendor, "USB Vendor ID"); + +static ushort idProduct; +module_param(idProduct, ushort, 0); +MODULE_PARM_DESC(idProduct, "USB Product ID"); + +static ushort bcdDevice; +module_param(bcdDevice, ushort, 0); +MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); + +static char *iManufacturer; +module_param(iManufacturer, charp, 0); +MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); + +static char *iProduct; +module_param(iProduct, charp, 0); +MODULE_PARM_DESC(iProduct, "USB Product string"); + +static char *iSerialNumber; +module_param(iSerialNumber, charp, 0); +MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); + +/*-------------------------------------------------------------------------*/ + +/** + * usb_add_function() - add a function to a configuration + * @config: the configuration + * @function: the function being added + * Context: single threaded during gadget setup + * + * After initialization, each configuration must have one or more + * functions added to it. Adding a function involves calling its @bind() + * method to allocate resources such as interface and string identifiers + * and endpoints. + * + * This function returns the value of the function's bind(), which is + * zero for success else a negative errno value. + */ +int __init usb_add_function(struct usb_configuration *config, + struct usb_function *function) +{ + int value = -EINVAL; + + DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", + function->name, function, + config->label, config); + + if (!function->set_alt || !function->disable) + goto done; + + function->config = config; + list_add_tail(&function->list, &config->functions); + + /* REVISIT *require* function->bind? */ + if (function->bind) { + value = function->bind(config, function); + if (value < 0) { + list_del(&function->list); + function->config = NULL; + } + } else + value = 0; + + /* We allow configurations that don't work at both speeds. + * If we run into a lowspeed Linux system, treat it the same + * as full speed ... it's the function drivers that will need + * to avoid bulk and ISO transfers. + */ + if (!config->fullspeed && function->descriptors) + config->fullspeed = true; + if (!config->highspeed && function->hs_descriptors) + config->highspeed = true; + +done: + if (value) + DBG(config->cdev, "adding '%s'/%p --> %d\n", + function->name, function, value); + return value; +} + +/** + * usb_interface_id() - allocate an unused interface ID + * @config: configuration associated with the interface + * @function: function handling the interface + * Context: single threaded during gadget setup + * + * usb_interface_id() is called from usb_function.bind() callbacks to + * allocate new interface IDs. The function driver will then store that + * ID in interface, association, CDC union, and other descriptors. It + * will also handle any control requests targetted at that interface, + * particularly changing its altsetting via set_alt(). There may + * also be class-specific or vendor-specific requests to handle. + * + * All interface identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. Note that since interface + * identifers are configuration-specific, functions used in more than + * one configuration (or more than once in a given configuration) need + * multiple versions of the relevant descriptors. + * + * Returns the interface ID which was allocated; or -ENODEV if no + * more interface IDs can be allocated. + */ +int __init usb_interface_id(struct usb_configuration *config, + struct usb_function *function) +{ + unsigned id = config->next_interface_id; + + if (id < MAX_CONFIG_INTERFACES) { + config->interface[id] = function; + config->next_interface_id = id + 1; + return id; + } + return -ENODEV; +} + +static int config_buf(struct usb_configuration *config, + enum usb_device_speed speed, void *buf, u8 type) +{ + struct usb_config_descriptor *c = buf; + void *next = buf + USB_DT_CONFIG_SIZE; + int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; + struct usb_function *f; + int status; + + /* write the config descriptor */ + c = buf; + c->bLength = USB_DT_CONFIG_SIZE; + c->bDescriptorType = type; + /* wTotalLength is written later */ + c->bNumInterfaces = config->next_interface_id; + c->bConfigurationValue = config->bConfigurationValue; + c->iConfiguration = config->iConfiguration; + c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; + c->bMaxPower = config->bMaxPower; + + /* There may be e.g. OTG descriptors */ + if (config->descriptors) { + status = usb_descriptor_fillbuf(next, len, + config->descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + /* add each function's descriptors */ + list_for_each_entry(f, &config->functions, list) { + struct usb_descriptor_header **descriptors; + + if (speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + if (!descriptors) + continue; + status = usb_descriptor_fillbuf(next, len, + (const struct usb_descriptor_header **) descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + len = next - buf; + c->wTotalLength = cpu_to_le16(len); + return len; +} + +static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + u8 type = w_value >> 8; + enum usb_device_speed speed = USB_SPEED_UNKNOWN; + + if (gadget_is_dualspeed(gadget)) { + int hs = 0; + + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + if (hs) + speed = USB_SPEED_HIGH; + + } + + /* This is a lookup by config *INDEX* */ + w_value &= 0xff; + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (speed == USB_SPEED_HIGH) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + if (w_value == 0) + return config_buf(c, speed, cdev->req->buf, type); + w_value--; + } + return -EINVAL; +} + +static int count_configs(struct usb_composite_dev *cdev, unsigned type) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + unsigned count = 0; + int hs = 0; + + if (gadget_is_dualspeed(gadget)) { + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_DEVICE_QUALIFIER) + hs = !hs; + } + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (hs) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + count++; + } + return count; +} + +static void device_qual(struct usb_composite_dev *cdev) +{ + struct usb_qualifier_descriptor *qual = cdev->req->buf; + + qual->bLength = sizeof(*qual); + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; + /* POLICY: same bcdUSB and device type info at both speeds */ + qual->bcdUSB = cdev->desc.bcdUSB; + qual->bDeviceClass = cdev->desc.bDeviceClass; + qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; + qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; + /* ASSUME same EP0 fifo size at both speeds */ + qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); +} + +/*-------------------------------------------------------------------------*/ + +static void reset_config(struct usb_composite_dev *cdev) +{ + struct usb_function *f; + + DBG(cdev, "reset config\n"); + + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->disable) + f->disable(f); + } + cdev->config = NULL; +} + +static int set_config(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl, unsigned number) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c = NULL; + int result = -EINVAL; + unsigned power = gadget_is_otg(gadget) ? 8 : 100; + int tmp; + + if (cdev->config) + reset_config(cdev); + + if (number) { + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == number) { + result = 0; + break; + } + } + if (result < 0) + goto done; + } else + result = 0; + + INFO(cdev, "%s speed config #%d: %s\n", + ({ char *speed; + switch (gadget->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + } ; speed; }), number, c ? c->label : "unconfigured"); + + if (!c) + goto done; + + cdev->config = c; + + /* Initialize all interfaces by setting them to altsetting zero. */ + for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { + struct usb_function *f = c->interface[tmp]; + + if (!f) + break; + + result = f->set_alt(f, tmp, 0); + if (result < 0) { + DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", + tmp, f->name, f, result); + + reset_config(cdev); + goto done; + } + } + + /* when we return, be sure our power usage is valid */ + power = 2 * c->bMaxPower; +done: + usb_gadget_vbus_draw(gadget, power); + return result; +} + +/** + * usb_add_config() - add a configuration to a device. + * @cdev: wraps the USB gadget + * @config: the configuration, with bConfigurationValue assigned + * Context: single threaded during gadget setup + * + * One of the main tasks of a composite driver's bind() routine is to + * add each of the configurations it supports, using this routine. + * + * This function returns the value of the configuration's bind(), which + * is zero for success else a negative errno value. Binding configurations + * assigns global resources including string IDs, and per-configuration + * resources such as interface IDs and endpoints. + */ +int __init usb_add_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + int status = -EINVAL; + struct usb_configuration *c; + + DBG(cdev, "adding config #%u '%s'/%p\n", + config->bConfigurationValue, + config->label, config); + + if (!config->bConfigurationValue || !config->bind) + goto done; + + /* Prevent duplicate configuration identifiers */ + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == config->bConfigurationValue) { + status = -EBUSY; + goto done; + } + } + + config->cdev = cdev; + list_add_tail(&config->list, &cdev->configs); + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; + + status = config->bind(config); + if (status < 0) { + list_del(&config->list); + config->cdev = NULL; + } else { + unsigned i; + + DBG(cdev, "cfg %d/%p speeds:%s%s\n", + config->bConfigurationValue, config, + config->highspeed ? " high" : "", + config->fullspeed + ? (gadget_is_dualspeed(cdev->gadget) + ? " full" + : " full/low") + : ""); + + for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { + struct usb_function *f = config->interface[i]; + + if (!f) + continue; + DBG(cdev, " interface %d = %s/%p\n", + i, f->name, f); + } + } + + /* set_alt(), or next config->bind(), sets up + * ep->driver_data as needed. + */ + usb_ep_autoconfig_reset(cdev->gadget); + +done: + if (status) + DBG(cdev, "added config '%s'/%u --> %d\n", config->label, + config->bConfigurationValue, status); + return status; +} + +/*-------------------------------------------------------------------------*/ + +/* We support strings in multiple languages ... string descriptor zero + * says which languages are supported. The typical case will be that + * only one language (probably English) is used, with I18N handled on + * the host side. + */ + +static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) +{ + const struct usb_gadget_strings *s; + u16 language; + __le16 *tmp; + + while (*sp) { + s = *sp; + language = cpu_to_le16(s->language); + for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { + if (*tmp == language) + goto repeat; + } + *tmp++ = language; +repeat: + sp++; + } +} + +static int lookup_string( + struct usb_gadget_strings **sp, + void *buf, + u16 language, + int id +) +{ + struct usb_gadget_strings *s; + int value; + + while (*sp) { + s = *sp++; + if (s->language != language) + continue; + value = usb_gadget_get_string(s, id, buf); + if (value > 0) + return value; + } + return -EINVAL; +} + +static int get_string(struct usb_composite_dev *cdev, + void *buf, u16 language, int id) +{ + struct usb_configuration *c; + struct usb_function *f; + int len; + + /* Yes, not only is USB's I18N support probably more than most + * folk will ever care about ... also, it's all supported here. + * (Except for UTF8 support for Unicode's "Astral Planes".) + */ + + /* 0 == report all available language codes */ + if (id == 0) { + struct usb_string_descriptor *s = buf; + struct usb_gadget_strings **sp; + + memset(s, 0, 256); + s->bDescriptorType = USB_DT_STRING; + + sp = composite->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(c, &cdev->configs, list) { + sp = c->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(f, &c->functions, list) { + sp = f->strings; + if (sp) + collect_langs(sp, s->wData); + } + } + + for (len = 0; s->wData[len] && len <= 126; len++) + continue; + if (!len) + return -EINVAL; + + s->bLength = 2 * (len + 1); + return s->bLength; + } + + /* Otherwise, look up and return a specified string. String IDs + * are device-scoped, so we look up each string table we're told + * about. These lookups are infrequent; simpler-is-better here. + */ + if (composite->strings) { + len = lookup_string(composite->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(c, &cdev->configs, list) { + if (c->strings) { + len = lookup_string(c->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(f, &c->functions, list) { + if (!f->strings) + continue; + len = lookup_string(f->strings, buf, language, id); + if (len > 0) + return len; + } + } + return -EINVAL; +} + +/** + * usb_string_id() - allocate an unused string ID + * @cdev: the device whose string descriptor IDs are being allocated + * Context: single threaded during gadget setup + * + * @usb_string_id() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. + */ +int __init usb_string_id(struct usb_composite_dev *cdev) +{ + if (cdev->next_string_id < 254) { + /* string id 0 is reserved */ + cdev->next_string_id++; + return cdev->next_string_id; + } + return -ENODEV; +} + +/*-------------------------------------------------------------------------*/ + +static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DBG((struct usb_composite_dev *) ep->driver_data, + "setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +/* + * The setup() callback implements all the ep0 functionality that's + * not handled lower down, in hardware or the hardware driver(like + * device and endpoint feature flags, and their status). It's all + * housekeeping for the gadget function we're implementing. Most of + * the work is in config and function specific setup. + */ +static int +composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + struct usb_function *f = NULL; + + /* partial re-init of the response message; the function or the + * gadget might need to intercept e.g. a control-OUT completion + * when we delegate to it. + */ + req->zero = 0; + req->complete = composite_setup_complete; + req->length = USB_BUFSIZ; + gadget->ep0->driver_data = cdev; + + switch (ctrl->bRequest) { + + /* we handle all standard USB descriptors */ + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + switch (w_value >> 8) { + + case USB_DT_DEVICE: + cdev->desc.bNumConfigurations = + count_configs(cdev, USB_DT_DEVICE); + value = min(w_length, (u16) sizeof cdev->desc); + memcpy(req->buf, &cdev->desc, value); + break; + case USB_DT_DEVICE_QUALIFIER: + if (!gadget_is_dualspeed(gadget)) + break; + device_qual(cdev); + value = min_t(int, w_length, + sizeof(struct usb_qualifier_descriptor)); + break; + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget_is_dualspeed(gadget)) + break; + /* FALLTHROUGH */ + case USB_DT_CONFIG: + value = config_desc(cdev, w_value); + if (value >= 0) + value = min(w_length, (u16) value); + break; + case USB_DT_STRING: + value = get_string(cdev, req->buf, + w_index, w_value & 0xff); + if (value >= 0) + value = min(w_length, (u16) value); + break; + } + break; + + /* any number of configs can work */ + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + goto unknown; + if (gadget_is_otg(gadget)) { + if (gadget->a_hnp_support) + DBG(cdev, "HNP available\n"); + else if (gadget->a_alt_hnp_support) + DBG(cdev, "HNP on another port\n"); + else + VDBG(cdev, "HNP inactive\n"); + } + spin_lock(&cdev->lock); + value = set_config(cdev, ctrl, w_value); + spin_unlock(&cdev->lock); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + if (cdev->config) + *(u8 *)req->buf = cdev->config->bConfigurationValue; + else + *(u8 *)req->buf = 0; + value = min(w_length, (u16) 1); + break; + + /* function drivers must handle get/set altsetting; if there's + * no get() method, we know only altsetting zero works. + */ + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[w_index]; + if (!f) + break; + if (w_value && !f->get_alt) + break; + value = f->set_alt(f, w_index, w_value); + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[w_index]; + if (!f) + break; + /* lots of interfaces only need altsetting zero... */ + value = f->get_alt ? f->get_alt(f, w_index) : 0; + if (value < 0) + break; + *((u8 *)req->buf) = value; + value = min(w_length, (u16) 1); + break; + default: +unknown: + VDBG(cdev, + "non-core control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* functions always handle their interfaces ... punt other + * recipients (endpoint, other, WUSB, ...) to the current + * configuration code. + * + * REVISIT it could make sense to let the composite device + * take such requests too, if that's ever needed: to work + * in config 0, etc. + */ + if ((ctrl->bRequestType & USB_RECIP_MASK) + == USB_RECIP_INTERFACE) { + f = cdev->config->interface[w_index]; + if (f && f->setup) + value = f->setup(f, ctrl); + else + f = NULL; + } + if (value < 0 && !f) { + struct usb_configuration *c; + + c = cdev->config; + if (c && c->setup) + value = c->setup(c, ctrl); + } + + goto done; + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + } + +done: + /* device either stalls (value < 0) or reports success */ + return value; +} + +static void composite_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + + /* REVISIT: should we have config and device level + * disconnect callbacks? + */ + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + reset_config(cdev); + spin_unlock_irqrestore(&cdev->lock, flags); +} + +/*-------------------------------------------------------------------------*/ + +static void /* __init_or_exit */ +composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + + /* composite_disconnect() must already have been called + * by the underlying peripheral controller driver! + * so there's no i/o concurrency that could affect the + * state protected by cdev->lock. + */ + WARN_ON(cdev->config); + + while (!list_empty(&cdev->configs)) { + struct usb_configuration *c; + + c = list_first_entry(&cdev->configs, + struct usb_configuration, list); + while (!list_empty(&c->functions)) { + struct usb_function *f; + + f = list_first_entry(&c->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + DBG(cdev, "unbind function '%s'/%p\n", + f->name, f); + f->unbind(c, f); + /* may free memory for "f" */ + } + } + list_del(&c->list); + if (c->unbind) { + DBG(cdev, "unbind config '%s'/%p\n", c->label, c); + c->unbind(c); + /* may free memory for "c" */ + } + } + if (composite->unbind) + composite->unbind(cdev); + + if (cdev->req) { + kfree(cdev->req->buf); + usb_ep_free_request(gadget->ep0, cdev->req); + } + kfree(cdev); + set_gadget_data(gadget, NULL); + composite = NULL; +} + +static void __init +string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) +{ + struct usb_string *str = tab->strings; + + for (str = tab->strings; str->s; str++) { + if (str->id == id) { + str->s = s; + return; + } + } +} + +static void __init +string_override(struct usb_gadget_strings **tab, u8 id, const char *s) +{ + while (*tab) { + string_override_one(*tab, id, s); + tab++; + } +} + +static int __init composite_bind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + int status = -ENOMEM; + + cdev = kzalloc(sizeof *cdev, GFP_KERNEL); + if (!cdev) + return status; + + spin_lock_init(&cdev->lock); + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + INIT_LIST_HEAD(&cdev->configs); + + /* preallocate control response and buffer */ + cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!cdev->req) + goto fail; + cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); + if (!cdev->req->buf) + goto fail; + cdev->req->complete = composite_setup_complete; + gadget->ep0->driver_data = cdev; + + cdev->bufsiz = USB_BUFSIZ; + cdev->driver = composite; + + usb_gadget_set_selfpowered(gadget); + + /* interface and string IDs start at zero via kzalloc. + * we force endpoints to start unassigned; few controller + * drivers will zero ep->driver_data. + */ + usb_ep_autoconfig_reset(cdev->gadget); + + /* composite gadget needs to assign strings for whole device (like + * serial number), register function drivers, potentially update + * power state and consumption, etc + */ + status = composite->bind(cdev); + if (status < 0) + goto fail; + + cdev->desc = *composite->dev; + cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + + /* standardized runtime overrides for device ID data */ + if (idVendor) + cdev->desc.idVendor = cpu_to_le16(idVendor); + if (idProduct) + cdev->desc.idProduct = cpu_to_le16(idProduct); + if (bcdDevice) + cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); + + /* strings can't be assigned before bind() allocates the + * releavnt identifiers + */ + if (cdev->desc.iManufacturer && iManufacturer) + string_override(composite->strings, + cdev->desc.iManufacturer, iManufacturer); + if (cdev->desc.iProduct && iProduct) + string_override(composite->strings, + cdev->desc.iProduct, iProduct); + if (cdev->desc.iSerialNumber && iSerialNumber) + string_override(composite->strings, + cdev->desc.iSerialNumber, iSerialNumber); + + INFO(cdev, "%s ready\n", composite->name); + return 0; + +fail: + composite_unbind(gadget); + return status; +} + +/*-------------------------------------------------------------------------*/ + +static void +composite_suspend(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config and device level + * suspend/resume callbacks? + */ + DBG(cdev, "suspend\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->suspend) + f->suspend(f); + } + } +} + +static void +composite_resume(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config and device level + * suspend/resume callbacks? + */ + DBG(cdev, "resume\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->resume) + f->resume(f); + } + } +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_gadget_driver composite_driver = { + .speed = USB_SPEED_HIGH, + + .bind = composite_bind, + .unbind = __exit_p(composite_unbind), + + .setup = composite_setup, + .disconnect = composite_disconnect, + + .suspend = composite_suspend, + .resume = composite_resume, + + .driver = { + .owner = THIS_MODULE, + }, +}; + +/** + * usb_composite_register() - register a composite driver + * @driver: the driver to register + * Context: single threaded during gadget setup + * + * This function is used to register drivers using the composite driver + * framework. The return value is zero, or a negative errno value. + * Those values normally come from the driver's @bind method, which does + * all the work of setting up the driver to match the hardware. + * + * On successful return, the gadget is ready to respond to requests from + * the host, unless one of its components invokes usb_gadget_disconnect() + * while it was binding. That would usually be done in order to wait for + * some userspace participation. + */ +int __init usb_composite_register(struct usb_composite_driver *driver) +{ + if (!driver || !driver->dev || !driver->bind || composite) + return -EINVAL; + + if (!driver->name) + driver->name = "composite"; + composite_driver.function = (char *) driver->name; + composite_driver.driver.name = driver->name; + composite = driver; + + return usb_gadget_register_driver(&composite_driver); +} + +/** + * usb_composite_unregister() - unregister a composite driver + * @driver: the driver to unregister + * + * This function is used to unregister drivers using the composite + * driver framework. + */ +void __exit usb_composite_unregister(struct usb_composite_driver *driver) +{ + if (composite != driver) + return; + usb_gadget_unregister_driver(&composite_driver); +} diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h new file mode 100644 index 000000000000..747c3a49cdc9 --- /dev/null +++ b/include/linux/usb/composite.h @@ -0,0 +1,338 @@ +/* + * composite.h -- framework for usb gadgets which are composite devices + * + * Copyright (C) 2006-2008 David Brownell + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LINUX_USB_COMPOSITE_H +#define __LINUX_USB_COMPOSITE_H + +/* + * This framework is an optional layer on top of the USB Gadget interface, + * making it easier to build (a) Composite devices, supporting multiple + * functions within any single configuration, and (b) Multi-configuration + * devices, also supporting multiple functions but without necessarily + * having more than one function per configuration. + * + * Example: a device with a single configuration supporting both network + * link and mass storage functions is a composite device. Those functions + * might alternatively be packaged in individual configurations, but in + * the composite model the host can use both functions at the same time. + */ + +#include +#include + + +struct usb_configuration; + +/** + * struct usb_function - describes one function of a configuration + * @name: For diagnostics, identifies the function. + * @strings: tables of strings, keyed by identifiers assigned during bind() + * and by language IDs provided in control requests + * @descriptors: Table of full (or low) speed descriptors, using interface and + * string identifiers assigned during @bind(). If this pointer is null, + * the function will not be available at full speed (or at low speed). + * @hs_descriptors: Table of high speed descriptors, using interface and + * string identifiers assigned during @bind(). If this pointer is null, + * the function will not be available at high speed. + * @config: assigned when @usb_add_function() is called; this is the + * configuration with which this function is associated. + * @bind: Before the gadget can register, all of its functions bind() to the + * available resources including string and interface identifiers used + * in interface or class descriptors; endpoints; I/O buffers; and so on. + * @unbind: Reverses @bind; called as a side effect of unregistering the + * driver which added this function. + * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may + * initialize usb_ep.driver data at this time (when it is used). + * Note that setting an interface to its current altsetting resets + * interface state, and that all interfaces have a disabled state. + * @get_alt: Returns the active altsetting. If this is not provided, + * then only altsetting zero is supported. + * @disable: (REQUIRED) Indicates the function should be disabled. Reasons + * include host resetting or reconfiguring the gadget, and disconnection. + * @setup: Used for interface-specific control requests. + * @suspend: Notifies functions when the host stops sending USB traffic. + * @resume: Notifies functions when the host restarts USB traffic. + * + * A single USB function uses one or more interfaces, and should in most + * cases support operation at both full and high speeds. Each function is + * associated by @usb_add_function() with a one configuration; that function + * causes @bind() to be called so resources can be allocated as part of + * setting up a gadget driver. Those resources include endpoints, which + * should be allocated using @usb_ep_autoconfig(). + * + * To support dual speed operation, a function driver provides descriptors + * for both high and full speed operation. Except in rare cases that don't + * involve bulk endpoints, each speed needs different endpoint descriptors. + * + * Function drivers choose their own strategies for managing instance data. + * The simplest strategy just declares it "static', which means the function + * can only be activated once. If the function needs to be exposed in more + * than one configuration at a given speed, it needs to support multiple + * usb_function structures (one for each configuration). + * + * A more complex strategy might encapsulate a @usb_function structure inside + * a driver-specific instance structure to allows multiple activations. An + * example of multiple activations might be a CDC ACM function that supports + * two or more distinct instances within the same configuration, providing + * several independent logical data links to a USB host. + */ +struct usb_function { + const char *name; + struct usb_gadget_strings **strings; + struct usb_descriptor_header **descriptors; + struct usb_descriptor_header **hs_descriptors; + + struct usb_configuration *config; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching. + * Related: unbind() may kfree() but bind() won't... + */ + + /* configuration management: bind/unbind */ + int (*bind)(struct usb_configuration *, + struct usb_function *); + void (*unbind)(struct usb_configuration *, + struct usb_function *); + + /* runtime state management */ + int (*set_alt)(struct usb_function *, + unsigned interface, unsigned alt); + int (*get_alt)(struct usb_function *, + unsigned interface); + void (*disable)(struct usb_function *); + int (*setup)(struct usb_function *, + const struct usb_ctrlrequest *); + void (*suspend)(struct usb_function *); + void (*resume)(struct usb_function *); + + /* internals */ + struct list_head list; +}; + +int usb_add_function(struct usb_configuration *, struct usb_function *); + +int usb_interface_id(struct usb_configuration *, struct usb_function *); + +/** + * ep_choose - select descriptor endpoint at current device speed + * @g: gadget, connected and running at some speed + * @hs: descriptor to use for high speed operation + * @fs: descriptor to use for full or low speed operation + */ +static inline struct usb_endpoint_descriptor * +ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *fs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */ + +/** + * struct usb_configuration - represents one gadget configuration + * @label: For diagnostics, describes the configuration. + * @strings: Tables of strings, keyed by identifiers assigned during @bind() + * and by language IDs provided in control requests. + * @descriptors: Table of descriptors preceding all function descriptors. + * Examples include OTG and vendor-specific descriptors. + * @bind: Called from @usb_add_config() to allocate resources unique to this + * configuration and to call @usb_add_function() for each function used. + * @unbind: Reverses @bind; called as a side effect of unregistering the + * driver which added this configuration. + * @setup: Used to delegate control requests that aren't handled by standard + * device infrastructure or directed at a specific interface. + * @bConfigurationValue: Copied into configuration descriptor. + * @iConfiguration: Copied into configuration descriptor. + * @bmAttributes: Copied into configuration descriptor. + * @bMaxPower: Copied into configuration descriptor. + * @cdev: assigned by @usb_add_config() before calling @bind(); this is + * the device associated with this configuration. + * + * Configurations are building blocks for gadget drivers structured around + * function drivers. Simple USB gadgets require only one function and one + * configuration, and handle dual-speed hardware by always providing the same + * functionality. Slightly more complex gadgets may have more than one + * single-function configuration at a given speed; or have configurations + * that only work at one speed. + * + * Composite devices are, by definition, ones with configurations which + * include more than one function. + * + * The lifecycle of a usb_configuration includes allocation, initialization + * of the fields described above, and calling @usb_add_config() to set up + * internal data and bind it to a specific device. The configuration's + * @bind() method is then used to initialize all the functions and then + * call @usb_add_function() for them. + * + * Those functions would normally be independant of each other, but that's + * not mandatory. CDC WMC devices are an example where functions often + * depend on other functions, with some functions subsidiary to others. + * Such interdependency may be managed in any way, so long as all of the + * descriptors complete by the time the composite driver returns from + * its bind() routine. + */ +struct usb_configuration { + const char *label; + struct usb_gadget_strings **strings; + const struct usb_descriptor_header **descriptors; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching... + */ + + /* configuration management: bind/unbind */ + int (*bind)(struct usb_configuration *); + void (*unbind)(struct usb_configuration *); + int (*setup)(struct usb_configuration *, + const struct usb_ctrlrequest *); + + /* fields in the config descriptor */ + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 bMaxPower; + + struct usb_composite_dev *cdev; + + /* internals */ + struct list_head list; + struct list_head functions; + u8 next_interface_id; + unsigned highspeed:1; + unsigned fullspeed:1; + struct usb_function *interface[MAX_CONFIG_INTERFACES]; +}; + +int usb_add_config(struct usb_composite_dev *, + struct usb_configuration *); + +/** + * struct usb_composite_driver - groups configurations into a gadget + * @name: For diagnostics, identifies the driver. + * @dev: Template descriptor for the device, including default device + * identifiers. + * @strings: tables of strings, keyed by identifiers assigned during bind() + * and language IDs provided in control requests + * @bind: (REQUIRED) Used to allocate resources that are shared across the + * whole device, such as string IDs, and add its configurations using + * @usb_add_config(). This may fail by returning a negative errno + * value; it should return zero on successful initialization. + * @unbind: Reverses @bind(); called as a side effect of unregistering + * this driver. + * + * Devices default to reporting self powered operation. Devices which rely + * on bus powered operation should report this in their @bind() method. + * + * Before returning from @bind, various fields in the template descriptor + * may be overridden. These include the idVendor/idProduct/bcdDevice values + * normally to bind the appropriate host side driver, and the three strings + * (iManufacturer, iProduct, iSerialNumber) normally used to provide user + * meaningful device identifiers. (The strings will not be defined unless + * they are defined in @dev and @strings.) The correct ep0 maxpacket size + * is also reported, as defined by the underlying controller driver. + */ +struct usb_composite_driver { + const char *name; + const struct usb_device_descriptor *dev; + struct usb_gadget_strings **strings; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching... + */ + + int (*bind)(struct usb_composite_dev *); + int (*unbind)(struct usb_composite_dev *); +}; + +extern int usb_composite_register(struct usb_composite_driver *); +extern void usb_composite_unregister(struct usb_composite_driver *); + + +/** + * struct usb_composite_device - represents one composite usb gadget + * @gadget: read-only, abstracts the gadget's usb peripheral controller + * @req: used for control responses; buffer is pre-allocated + * @bufsiz: size of buffer pre-allocated in @req + * @config: the currently active configuration + * + * One of these devices is allocated and initialized before the + * associated device driver's bind() is called. + * + * OPEN ISSUE: it appears that some WUSB devices will need to be + * built by combining a normal (wired) gadget with a wireless one. + * This revision of the gadget framework should probably try to make + * sure doing that won't hurt too much. + * + * One notion for how to handle Wireless USB devices involves: + * (a) a second gadget here, discovery mechanism TBD, but likely + * needing separate "register/unregister WUSB gadget" calls; + * (b) updates to usb_gadget to include flags "is it wireless", + * "is it wired", plus (presumably in a wrapper structure) + * bandgroup and PHY info; + * (c) presumably a wireless_ep wrapping a usb_ep, and reporting + * wireless-specific parameters like maxburst and maxsequence; + * (d) configurations that are specific to wireless links; + * (e) function drivers that understand wireless configs and will + * support wireless for (additional) function instances; + * (f) a function to support association setup (like CBAF), not + * necessarily requiring a wireless adapter; + * (g) composite device setup that can create one or more wireless + * configs, including appropriate association setup support; + * (h) more, TBD. + */ +struct usb_composite_dev { + struct usb_gadget *gadget; + struct usb_request *req; + unsigned bufsiz; + + struct usb_configuration *config; + + /* internals */ + struct usb_device_descriptor desc; + struct list_head configs; + struct usb_composite_driver *driver; + u8 next_string_id; + + spinlock_t lock; + + /* REVISIT use and existence of lock ... */ +}; + +extern int usb_string_id(struct usb_composite_dev *c); + +/* messaging utils */ +#define DBG(d, fmt, args...) \ + dev_dbg(&(d)->gadget->dev , fmt , ## args) +#define VDBG(d, fmt, args...) \ + dev_vdbg(&(d)->gadget->dev , fmt , ## args) +#define ERROR(d, fmt, args...) \ + dev_err(&(d)->gadget->dev , fmt , ## args) +#define WARN(d, fmt, args...) \ + dev_warn(&(d)->gadget->dev , fmt , ## args) +#define INFO(d, fmt, args...) \ + dev_info(&(d)->gadget->dev , fmt , ## args) + +#endif /* __LINUX_USB_COMPOSITE_H */ -- cgit From e0d795e4f36c2d8949c1355b497fc5425dbb9437 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 3 Jun 2008 14:47:52 +0300 Subject: usb: irda: cleanup on ir-usb module General cleanup on ir-usb module. Introduced a common header that could be used also on usb gadget framework. Lot's of cleanups and now using macros from the header file. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ir-usb.c | 490 ++++++++++++++++++++++++-------------------- include/linux/usb/irda.h | 151 ++++++++++++++ 2 files changed, 421 insertions(+), 220 deletions(-) create mode 100644 include/linux/usb/irda.h (limited to 'include/linux') diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 004d57385a75..0063c11c8081 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -19,7 +19,12 @@ * was written by Roman Weissgaerber , Dag Brattli * , and Jean Tourrilhes * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver + * + * 2008_Jun_02 Felipe Balbi + * Introduced common header to be used also in USB Gadget Framework. + * Still needs some other style fixes. * * 2007_Jun_21 Alan Cox * Minimal cleanups for some of the driver problens and tty layer abuse. @@ -59,9 +64,10 @@ #include #include #include -#include +#include #include #include +#include /* * Version Information @@ -70,100 +76,75 @@ #define DRIVER_AUTHOR "Greg Kroah-Hartman " #define DRIVER_DESC "USB IR Dongle driver" -/* USB IrDA class spec information */ -#define USB_CLASS_IRDA 0x02 -#define USB_DT_IRDA 0x21 -#define IU_REQ_GET_CLASS_DESC 0x06 -#define SPEED_2400 0x01 -#define SPEED_9600 0x02 -#define SPEED_19200 0x03 -#define SPEED_38400 0x04 -#define SPEED_57600 0x05 -#define SPEED_115200 0x06 -#define SPEED_576000 0x07 -#define SPEED_1152000 0x08 -#define SPEED_4000000 0x09 - -struct irda_class_desc { - u8 bLength; - u8 bDescriptorType; - u16 bcdSpecRevision; - u8 bmDataSize; - u8 bmWindowSize; - u8 bmMinTurnaroundTime; - u16 wBaudRate; - u8 bmAdditionalBOFs; - u8 bIrdaRateSniff; - u8 bMaxUnicastList; -} __attribute__ ((packed)); - static int debug; /* if overridden by the user, then use their value for the size of the read and * write urbs */ static int buffer_size; + /* if overridden by the user, then use the specified number of XBOFs */ static int xbof = -1; -static int ir_startup (struct usb_serial *serial); -static int ir_open (struct usb_serial_port *port, struct file *filep); -static void ir_close (struct usb_serial_port *port, struct file *filep); -static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static void ir_write_bulk_callback (struct urb *urb); -static void ir_read_bulk_callback (struct urb *urb); -static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); +static int ir_startup(struct usb_serial *serial); +static int ir_open(struct usb_serial_port *port, struct file *filep); +static void ir_close(struct usb_serial_port *port, struct file *filep); +static int ir_write(struct usb_serial_port *port, + const unsigned char *buf, int count); +static void ir_write_bulk_callback(struct urb *urb); +static void ir_read_bulk_callback(struct urb *urb); +static void ir_set_termios(struct usb_serial_port *port, + struct ktermios *old_termios); /* Not that this lot means you can only have one per system */ -static u8 ir_baud = 0; -static u8 ir_xbof = 0; -static u8 ir_add_bof = 0; +static u8 ir_baud; +static u8 ir_xbof; +static u8 ir_add_bof; -static struct usb_device_id id_table [] = { +static struct usb_device_id ir_id_table[] = { { USB_DEVICE(0x050f, 0x0180) }, /* KC Technology, KC-180 */ { USB_DEVICE(0x08e9, 0x0100) }, /* XTNDAccess */ { USB_DEVICE(0x09c4, 0x0011) }, /* ACTiSys ACT-IR2000U */ - { USB_INTERFACE_INFO (USB_CLASS_APP_SPEC, USB_CLASS_IRDA, 0) }, + { USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, USB_SUBCLASS_IRDA, 0) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, ir_id_table); static struct usb_driver ir_driver = { - .name = "ir-usb", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table, - .no_dynamic_id = 1, + .name = "ir-usb", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = ir_id_table, + .no_dynamic_id = 1, }; - static struct usb_serial_driver ir_device = { - .driver = { - .owner = THIS_MODULE, - .name = "ir-usb", + .driver = { + .owner = THIS_MODULE, + .name = "ir-usb", }, - .description = "IR Dongle", - .usb_driver = &ir_driver, - .id_table = id_table, - .num_ports = 1, - .set_termios = ir_set_termios, - .attach = ir_startup, - .open = ir_open, - .close = ir_close, - .write = ir_write, - .write_bulk_callback = ir_write_bulk_callback, - .read_bulk_callback = ir_read_bulk_callback, + .description = "IR Dongle", + .usb_driver = &ir_driver, + .id_table = ir_id_table, + .num_ports = 1, + .set_termios = ir_set_termios, + .attach = ir_startup, + .open = ir_open, + .close = ir_close, + .write = ir_write, + .write_bulk_callback = ir_write_bulk_callback, + .read_bulk_callback = ir_read_bulk_callback, }; -static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) +static inline void irda_usb_dump_class_desc(struct usb_irda_cs_descriptor *desc) { dbg("bLength=%x", desc->bLength); dbg("bDescriptorType=%x", desc->bDescriptorType); - dbg("bcdSpecRevision=%x", desc->bcdSpecRevision); + dbg("bcdSpecRevision=%x", __le16_to_cpu(desc->bcdSpecRevision)); dbg("bmDataSize=%x", desc->bmDataSize); dbg("bmWindowSize=%x", desc->bmWindowSize); dbg("bmMinTurnaroundTime=%d", desc->bmMinTurnaroundTime); - dbg("wBaudRate=%x", desc->wBaudRate); + dbg("wBaudRate=%x", __le16_to_cpu(desc->wBaudRate)); dbg("bmAdditionalBOFs=%x", desc->bmAdditionalBOFs); dbg("bIrdaRateSniff=%x", desc->bIrdaRateSniff); dbg("bMaxUnicastList=%x", desc->bMaxUnicastList); @@ -181,35 +162,37 @@ static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) * * Based on the same function in drivers/net/irda/irda-usb.c */ -static struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum) +static struct usb_irda_cs_descriptor * +irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum) { - struct irda_class_desc *desc; + struct usb_irda_cs_descriptor *desc; int ret; - - desc = kzalloc(sizeof (struct irda_class_desc), GFP_KERNEL); - if (desc == NULL) + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) return NULL; - - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), - IU_REQ_GET_CLASS_DESC, + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CS_IRDA_GET_CLASS_DESC, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, ifnum, desc, sizeof(*desc), 1000); - + dbg("%s - ret=%d", __func__, ret); if (ret < sizeof(*desc)) { dbg("%s - class descriptor read %s (%d)", __func__, - (ret<0) ? "failed" : "too short", + (ret < 0) ? "failed" : "too short", ret); goto error; } - if (desc->bDescriptorType != USB_DT_IRDA) { + if (desc->bDescriptorType != USB_DT_CS_IRDA) { dbg("%s - bad class descriptor type", __func__); goto error; } - + irda_usb_dump_class_desc(desc); return desc; + error: kfree(desc); return NULL; @@ -219,64 +202,100 @@ error: static u8 ir_xbof_change(u8 xbof) { u8 result; + /* reference irda-usb.c */ - switch(xbof) { - case 48: result = 0x10; break; - case 28: - case 24: result = 0x20; break; - default: - case 12: result = 0x30; break; - case 5: - case 6: result = 0x40; break; - case 3: result = 0x50; break; - case 2: result = 0x60; break; - case 1: result = 0x70; break; - case 0: result = 0x80; break; + switch (xbof) { + case 48: + result = 0x10; + break; + case 28: + case 24: + result = 0x20; + break; + default: + case 12: + result = 0x30; + break; + case 5: + case 6: + result = 0x40; + break; + case 3: + result = 0x50; + break; + case 2: + result = 0x60; + break; + case 1: + result = 0x70; + break; + case 0: + result = 0x80; + break; } + return(result); } -static int ir_startup (struct usb_serial *serial) +static int ir_startup(struct usb_serial *serial) { - struct irda_class_desc *irda_desc; + struct usb_irda_cs_descriptor *irda_desc; - irda_desc = irda_usb_find_class_desc (serial->dev, 0); - if (irda_desc == NULL) { - dev_err (&serial->dev->dev, "IRDA class descriptor not found, device not bound\n"); + irda_desc = irda_usb_find_class_desc(serial->dev, 0); + if (!irda_desc) { + dev_err(&serial->dev->dev, + "IRDA class descriptor not found, device not bound\n"); return -ENODEV; } - dbg ("%s - Baud rates supported:%s%s%s%s%s%s%s%s%s", + dbg("%s - Baud rates supported:%s%s%s%s%s%s%s%s%s", __func__, - (irda_desc->wBaudRate & 0x0001) ? " 2400" : "", - (irda_desc->wBaudRate & 0x0002) ? " 9600" : "", - (irda_desc->wBaudRate & 0x0004) ? " 19200" : "", - (irda_desc->wBaudRate & 0x0008) ? " 38400" : "", - (irda_desc->wBaudRate & 0x0010) ? " 57600" : "", - (irda_desc->wBaudRate & 0x0020) ? " 115200" : "", - (irda_desc->wBaudRate & 0x0040) ? " 576000" : "", - (irda_desc->wBaudRate & 0x0080) ? " 1152000" : "", - (irda_desc->wBaudRate & 0x0100) ? " 4000000" : ""); - - switch( irda_desc->bmAdditionalBOFs ) { - case 0x01: ir_add_bof = 48; break; - case 0x02: ir_add_bof = 24; break; - case 0x04: ir_add_bof = 12; break; - case 0x08: ir_add_bof = 6; break; - case 0x10: ir_add_bof = 3; break; - case 0x20: ir_add_bof = 2; break; - case 0x40: ir_add_bof = 1; break; - case 0x80: ir_add_bof = 0; break; - default:; + (irda_desc->wBaudRate & USB_IRDA_BR_2400) ? " 2400" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_9600) ? " 9600" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_19200) ? " 19200" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_38400) ? " 38400" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_57600) ? " 57600" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_115200) ? " 115200" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_576000) ? " 576000" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_1152000) ? " 1152000" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_4000000) ? " 4000000" : ""); + + switch (irda_desc->bmAdditionalBOFs) { + case USB_IRDA_AB_48: + ir_add_bof = 48; + break; + case USB_IRDA_AB_24: + ir_add_bof = 24; + break; + case USB_IRDA_AB_12: + ir_add_bof = 12; + break; + case USB_IRDA_AB_6: + ir_add_bof = 6; + break; + case USB_IRDA_AB_3: + ir_add_bof = 3; + break; + case USB_IRDA_AB_2: + ir_add_bof = 2; + break; + case USB_IRDA_AB_1: + ir_add_bof = 1; + break; + case USB_IRDA_AB_0: + ir_add_bof = 0; + break; + default: + break; } - kfree (irda_desc); + kfree(irda_desc); - return 0; + return 0; } -static int ir_open (struct usb_serial_port *port, struct file *filp) +static int ir_open(struct usb_serial_port *port, struct file *filp) { char *buffer; int result = 0; @@ -285,51 +304,55 @@ static int ir_open (struct usb_serial_port *port, struct file *filp) if (buffer_size) { /* override the default buffer sizes */ - buffer = kmalloc (buffer_size, GFP_KERNEL); + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { - dev_err (&port->dev, "%s - out of memory.\n", __func__); + dev_err(&port->dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - kfree (port->read_urb->transfer_buffer); + kfree(port->read_urb->transfer_buffer); port->read_urb->transfer_buffer = buffer; port->read_urb->transfer_buffer_length = buffer_size; - buffer = kmalloc (buffer_size, GFP_KERNEL); + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { - dev_err (&port->dev, "%s - out of memory.\n", __func__); + dev_err(&port->dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - kfree (port->write_urb->transfer_buffer); + kfree(port->write_urb->transfer_buffer); port->write_urb->transfer_buffer = buffer; port->write_urb->transfer_buffer_length = buffer_size; port->bulk_out_size = buffer_size; } /* Start reading from the device */ - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), + port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, ir_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); return result; } -static void ir_close (struct usb_serial_port *port, struct file * filp) +static void ir_close(struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); - + /* shutdown our bulk read */ usb_kill_urb(port->read_urb); } -static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int ir_write(struct usb_serial_port *port, + const unsigned char *buf, int count) { unsigned char *transfer_buffer; int result; @@ -338,7 +361,7 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int dbg("%s - port = %d, count = %d", __func__, port->number, count); if (!port->tty) { - dev_err (&port->dev, "%s - no tty???\n", __func__); + dev_err(&port->dev, "%s - no tty???\n", __func__); return 0; } @@ -359,7 +382,7 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int /* * The first byte of the packet we send to the device contains an - * inband header which indicates an additional number of BOFs and + * inbound header which indicates an additional number of BOFs and * a baud rate change. * * See section 5.4.2.2 of the USB IrDA spec. @@ -367,9 +390,9 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int *transfer_buffer = ir_xbof | ir_baud; ++transfer_buffer; - memcpy (transfer_buffer, buf, transfer_size); + memcpy(transfer_buffer, buf, transfer_size); - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->write_urb, port->serial->dev, usb_sndbulkpipe(port->serial->dev, @@ -381,17 +404,19 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int port->write_urb->transfer_flags = URB_ZERO_PACKET; - result = usb_submit_urb (port->write_urb, GFP_ATOMIC); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { port->write_urb_busy = 0; - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); } else result = transfer_size; return result; } -static void ir_write_bulk_callback (struct urb *urb) +static void ir_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int status = urb->status; @@ -405,7 +430,7 @@ static void ir_write_bulk_callback (struct urb *urb) return; } - usb_serial_debug_data ( + usb_serial_debug_data( debug, &port->dev, __func__, @@ -415,7 +440,7 @@ static void ir_write_bulk_callback (struct urb *urb) usb_serial_port_softint(port); } -static void ir_read_bulk_callback (struct urb *urb) +static void ir_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct tty_struct *tty; @@ -431,68 +456,69 @@ static void ir_read_bulk_callback (struct urb *urb) } switch (status) { - case 0: /* Successful */ - - /* - * The first byte of the packet we get from the device - * contains a busy indicator and baud rate change. - * See section 5.4.1.2 of the USB IrDA spec. - */ - if ((*data & 0x0f) > 0) - ir_baud = *data & 0x0f; - - usb_serial_debug_data ( - debug, - &port->dev, - __func__, - urb->actual_length, - data); - - tty = port->tty; - - if (tty_buffer_request_room(tty, urb->actual_length - 1)) { - tty_insert_flip_string(tty, data+1, urb->actual_length - 1); - tty_flip_buffer_push(tty); - } - - /* - * No break here. - * We want to resubmit the urb so we can read - * again. - */ - - case -EPROTO: /* taking inspiration from pl2303.c */ - - /* Continue trying to always read */ - usb_fill_bulk_urb ( - port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ir_read_bulk_callback, - port); - - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", - __func__, result); - - break ; - - default: - dbg("%s - nonzero read bulk status received: %d", - __func__, - status); - break ; + case 0: /* Successful */ + + /* + * The first byte of the packet we get from the device + * contains a busy indicator and baud rate change. + * See section 5.4.1.2 of the USB IrDA spec. + */ + if ((*data & 0x0f) > 0) + ir_baud = *data & 0x0f; + + usb_serial_debug_data( + debug, + &port->dev, + __func__, + urb->actual_length, + data); + + tty = port->tty; + + if (tty_buffer_request_room(tty, urb->actual_length - 1)) { + tty_insert_flip_string(tty, data + 1, + urb->actual_length - 1); + tty_flip_buffer_push(tty); + } + /* + * No break here. + * We want to resubmit the urb so we can read + * again. + */ + + case -EPROTO: /* taking inspiration from pl2303.c */ + + /* Continue trying to always read */ + usb_fill_bulk_urb( + port->read_urb, + port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ir_read_bulk_callback, + port); + + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + break; + + default: + dbg("%s - nonzero read bulk status received: %d", + __func__, + status); + break; } return; } -static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void ir_set_termios(struct usb_serial_port *port, + struct ktermios *old_termios) { unsigned char *transfer_buffer; int result; @@ -510,19 +536,36 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t */ switch (baud) { - case 2400: ir_baud = SPEED_2400; break; - case 9600: ir_baud = SPEED_9600; break; - case 19200: ir_baud = SPEED_19200; break; - case 38400: ir_baud = SPEED_38400; break; - case 57600: ir_baud = SPEED_57600; break; - case 115200: ir_baud = SPEED_115200; break; - case 576000: ir_baud = SPEED_576000; break; - case 1152000: ir_baud = SPEED_1152000; break; - case 4000000: ir_baud = SPEED_4000000; break; - break; - default: - ir_baud = SPEED_9600; - baud = 9600; + case 2400: + ir_baud = USB_IRDA_BR_2400; + break; + case 9600: + ir_baud = USB_IRDA_BR_9600; + break; + case 19200: + ir_baud = USB_IRDA_BR_19200; + break; + case 38400: + ir_baud = USB_IRDA_BR_38400; + break; + case 57600: + ir_baud = USB_IRDA_BR_57600; + break; + case 115200: + ir_baud = USB_IRDA_BR_115200; + break; + case 576000: + ir_baud = USB_IRDA_BR_576000; + break; + case 1152000: + ir_baud = USB_IRDA_BR_1152000; + break; + case 4000000: + ir_baud = USB_IRDA_BR_4000000; + break; + default: + ir_baud = USB_IRDA_BR_9600; + baud = 9600; } if (xbof == -1) @@ -538,10 +581,11 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t transfer_buffer = port->write_urb->transfer_buffer; *transfer_buffer = ir_xbof | ir_baud; - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->write_urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, 1, ir_write_bulk_callback, @@ -549,38 +593,44 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t port->write_urb->transfer_flags = URB_ZERO_PACKET; - result = usb_submit_urb (port->write_urb, GFP_KERNEL); + result = usb_submit_urb(port->write_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); /* Only speed changes are supported */ tty_termios_copy_hw(port->tty->termios, old_termios); tty_encode_baud_rate(port->tty, baud, baud); } - -static int __init ir_init (void) +static int __init ir_init(void) { int retval; + retval = usb_serial_register(&ir_device); if (retval) goto failed_usb_serial_register; + retval = usb_register(&ir_driver); - if (retval) + if (retval) goto failed_usb_register; + info(DRIVER_DESC " " DRIVER_VERSION); + return 0; + failed_usb_register: usb_serial_deregister(&ir_device); + failed_usb_serial_register: return retval; } - -static void __exit ir_exit (void) +static void __exit ir_exit(void) { - usb_deregister (&ir_driver); - usb_serial_deregister (&ir_device); + usb_deregister(&ir_driver); + usb_serial_deregister(&ir_device); } diff --git a/include/linux/usb/irda.h b/include/linux/usb/irda.h new file mode 100644 index 000000000000..e345ceaf72d6 --- /dev/null +++ b/include/linux/usb/irda.h @@ -0,0 +1,151 @@ +/* + * USB IrDA Bridge Device Definition + */ + +#ifndef __LINUX_USB_IRDA_H +#define __LINUX_USB_IRDA_H + +/* This device should use Application-specific class */ + +#define USB_SUBCLASS_IRDA 0x02 + +/*-------------------------------------------------------------------------*/ + +/* Class-Specific requests (bRequest field) */ + +#define USB_REQ_CS_IRDA_RECEIVING 1 +#define USB_REQ_CS_IRDA_CHECK_MEDIA_BUSY 3 +#define USB_REQ_CS_IRDA_RATE_SNIFF 4 +#define USB_REQ_CS_IRDA_UNICAST_LIST 5 +#define USB_REQ_CS_IRDA_GET_CLASS_DESC 6 + +/*-------------------------------------------------------------------------*/ + +/* Class-Specific descriptor */ + +#define USB_DT_CS_IRDA 0x21 + +/*-------------------------------------------------------------------------*/ + +/* Data sizes */ + +#define USB_IRDA_DS_2048 (1 << 5) +#define USB_IRDA_DS_1024 (1 << 4) +#define USB_IRDA_DS_512 (1 << 3) +#define USB_IRDA_DS_256 (1 << 2) +#define USB_IRDA_DS_128 (1 << 1) +#define USB_IRDA_DS_64 (1 << 0) + +/* Window sizes */ + +#define USB_IRDA_WS_7 (1 << 6) +#define USB_IRDA_WS_6 (1 << 5) +#define USB_IRDA_WS_5 (1 << 4) +#define USB_IRDA_WS_4 (1 << 3) +#define USB_IRDA_WS_3 (1 << 2) +#define USB_IRDA_WS_2 (1 << 1) +#define USB_IRDA_WS_1 (1 << 0) + +/* Min turnaround times in usecs */ + +#define USB_IRDA_MTT_0 (1 << 7) +#define USB_IRDA_MTT_10 (1 << 6) +#define USB_IRDA_MTT_50 (1 << 5) +#define USB_IRDA_MTT_100 (1 << 4) +#define USB_IRDA_MTT_500 (1 << 3) +#define USB_IRDA_MTT_1000 (1 << 2) +#define USB_IRDA_MTT_5000 (1 << 1) +#define USB_IRDA_MTT_10000 (1 << 0) + +/* Baud rates */ + +#define USB_IRDA_BR_4000000 (1 << 8) +#define USB_IRDA_BR_1152000 (1 << 7) +#define USB_IRDA_BR_576000 (1 << 6) +#define USB_IRDA_BR_115200 (1 << 5) +#define USB_IRDA_BR_57600 (1 << 4) +#define USB_IRDA_BR_38400 (1 << 3) +#define USB_IRDA_BR_19200 (1 << 2) +#define USB_IRDA_BR_9600 (1 << 1) +#define USB_IRDA_BR_2400 (1 << 0) + +/* Additional BOFs */ + +#define USB_IRDA_AB_0 (1 << 7) +#define USB_IRDA_AB_1 (1 << 6) +#define USB_IRDA_AB_2 (1 << 5) +#define USB_IRDA_AB_3 (1 << 4) +#define USB_IRDA_AB_6 (1 << 3) +#define USB_IRDA_AB_12 (1 << 2) +#define USB_IRDA_AB_24 (1 << 1) +#define USB_IRDA_AB_48 (1 << 0) + +/* IRDA Rate Sniff */ + +#define USB_IRDA_RATE_SNIFF 1 + +/*-------------------------------------------------------------------------*/ + +struct usb_irda_cs_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 bcdSpecRevision; + __u8 bmDataSize; + __u8 bmWindowSize; + __u8 bmMinTurnaroundTime; + __le16 wBaudRate; + __u8 bmAdditionalBOFs; + __u8 bIrdaRateSniff; + __u8 bMaxUnicastList; +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +/* Data Format */ + +#define USB_IRDA_STATUS_MEDIA_BUSY (1 << 7) + +/* The following is a 4-bit value used for both + * inbound and outbound headers: + * + * 0 - speed ignored + * 1 - 2400 bps + * 2 - 9600 bps + * 3 - 19200 bps + * 4 - 38400 bps + * 5 - 57600 bps + * 6 - 115200 bps + * 7 - 576000 bps + * 8 - 1.152 Mbps + * 9 - 5 mbps + * 10..15 - Reserved + */ +#define USB_IRDA_STATUS_LINK_SPEED 0x0f + +/* The following is a 4-bit value used only for + * outbound header: + * + * 0 - No change (BOF ignored) + * 1 - 48 BOFs + * 2 - 24 BOFs + * 3 - 12 BOFs + * 4 - 6 BOFs + * 5 - 3 BOFs + * 6 - 2 BOFs + * 7 - 1 BOFs + * 8 - 0 BOFs + * 9..15 - Reserved + */ +#define USB_IRDA_EXTRA_BOFS 0xf0 + +struct usb_irda_inbound_header { + __u8 bmStatus; +}; + +struct usb_irda_outbound_header { + __u8 bmChange; +}; + +#endif /* __LINUX_USB_IRDA_H */ + -- cgit From f579c2b46f74038e8f5a762c7f10c2385b33e3dc Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 2 Jun 2008 16:26:48 -0400 Subject: USB Gadget: documentation update This patch (as1102) clarifies two points in the USB Gadget kerneldoc: Request completion callbacks are always made with interrupts disabled; Device controllers may not support STALLing the status stage of a control transfer after the data stage is over. Signed-off-by: Alan Stern Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/gadget.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 0ebedaec075d..0460a746480c 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -33,7 +33,8 @@ struct usb_ep; * @short_not_ok: When reading data, makes short packets be * treated as errors (queue stops advancing till cleanup). * @complete: Function called when request completes, so this request and - * its buffer may be re-used. + * its buffer may be re-used. The function will always be called with + * interrupts disabled, and it must not sleep. * Reads terminate with a short packet, or when the buffer fills, * whichever comes first. When writes terminate, some data bytes * will usually still be in flight (often in a hardware fifo). @@ -271,7 +272,10 @@ static inline void usb_ep_free_request(struct usb_ep *ep, * (Note that some USB device controllers disallow protocol stall responses * in some cases.) When control responses are deferred (the response is * written after the setup callback returns), then usb_ep_set_halt() may be - * used on ep0 to trigger protocol stalls. + * used on ep0 to trigger protocol stalls. Depending on the controller, + * it may not be possible to trigger a status-stage protocol stall when the + * data stage is over, that is, from within the response's completion + * routine. * * For periodic endpoints, like interrupt or isochronous ones, the usb host * arranges to poll once per interval, and the gadget driver usually will -- cgit From 625f694936cbbdee98e6cc65f72724a7660e7946 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 15 Jun 2008 09:42:02 +0800 Subject: USB: remove interface parameter of usb_reset_composite_device From the current implementation of usb_reset_composite_device function, the iface parameter is no longer useful. This function doesn't do something special for the iface usb_interface,compared with other interfaces in the usb_device. So remove the parameter and fix the related caller. Signed-off-by: Ming Lei Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/hid/usbhid/hid-core.c | 2 +- drivers/usb/core/devio.c | 2 +- drivers/usb/core/hub.c | 11 +++-------- drivers/usb/storage/transport.c | 3 +-- include/linux/usb.h | 3 +-- 5 files changed, 7 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 01427c51c7cc..69fa79b6b51c 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -122,7 +122,7 @@ static void hid_reset(struct work_struct *work) dev_dbg(&usbhid->intf->dev, "resetting device\n"); rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); if (rc_lock >= 0) { - rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf); + rc = usb_reset_composite_device(hid_to_usb_dev(hid)); if (rc_lock) usb_unlock_device(hid_to_usb_dev(hid)); } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 7bee9c18b3bc..bc1cce5cf758 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -872,7 +872,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return usb_reset_composite_device(ps->dev, NULL); + return usb_reset_composite_device(ps->dev); } static int proc_setintf(struct dev_state *ps, void __user *arg) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3251120b414e..207c33d369be 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2712,7 +2712,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, #endif } else { - status = usb_reset_composite_device(udev, NULL); + status = usb_reset_composite_device(udev); } usb_unlock_device(udev); @@ -2940,7 +2940,7 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - ret = usb_reset_composite_device(hdev, intf); + ret = usb_reset_composite_device(hdev); if (ret) { dev_dbg (hub_dev, "error resetting hub: %d\n", ret); @@ -3355,7 +3355,6 @@ EXPORT_SYMBOL_GPL(usb_reset_device); /** * usb_reset_composite_device - warn interface drivers and perform a USB port reset * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) - * @iface: interface bound to the driver making the request (optional) * * Warns all drivers bound to registered interfaces (using their pre_reset * method), performs the port reset, and then lets the drivers know that @@ -3368,8 +3367,7 @@ EXPORT_SYMBOL_GPL(usb_reset_device); * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). */ -int usb_reset_composite_device(struct usb_device *udev, - struct usb_interface *iface) +int usb_reset_composite_device(struct usb_device *udev) { int ret; int i; @@ -3385,9 +3383,6 @@ int usb_reset_composite_device(struct usb_device *udev, /* Prevent autosuspend during the reset */ usb_autoresume_device(udev); - if (iface && iface->condition != USB_INTERFACE_BINDING) - iface = NULL; - if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { struct usb_interface *cintf = config->interface[i]; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 08d3a13fec2c..670e4cbd1f06 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1172,8 +1172,7 @@ int usb_stor_port_reset(struct us_data *us) result = -EIO; US_DEBUGP("No reset during disconnect\n"); } else { - result = usb_reset_composite_device( - us->pusb_dev, us->pusb_intf); + result = usb_reset_composite_device(us->pusb_dev); US_DEBUGP("usb_reset_composite_device returns %d\n", result); } diff --git a/include/linux/usb.h b/include/linux/usb.h index 8429d08bd2fd..c74cc64bddc8 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -497,8 +497,7 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, /* USB port reset for device reinitialization */ extern int usb_reset_device(struct usb_device *dev); -extern int usb_reset_composite_device(struct usb_device *dev, - struct usb_interface *iface); +extern int usb_reset_composite_device(struct usb_device *dev); extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); -- cgit From 742120c63138651c898614001cb58cd607401eac Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 18 Jun 2008 22:00:29 +0800 Subject: USB: fix usb_reset_device and usb_reset_composite_device(take 3) This patch renames the existing usb_reset_device in hub.c to usb_reset_and_verify_device and renames the existing usb_reset_composite_device to usb_reset_device. Also the new usb_reset_and_verify_device does't need to be EXPORTED . The idea of the patch is that external interface driver should warn the other interfaces' driver of the same device before and after reseting the usb device. One interface driver shoud call _old_ usb_reset_composite_device instead of _old_ usb_reset_device since it can't assume the device contains only one interface. The _old_ usb_reset_composite_device is safe for single interface device also. we rename the two functions to make the change easily. This patch is under guideline from Alan Stern. Signed-off-by: Ming Lei --- drivers/hid/usbhid/hid-core.c | 2 +- drivers/usb/core/devio.c | 2 +- drivers/usb/core/hub.c | 33 +++++++++++++++++---------------- drivers/usb/storage/transport.c | 4 ++-- include/linux/usb.h | 5 ++--- 5 files changed, 23 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 69fa79b6b51c..27fe4d8912cb 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -122,7 +122,7 @@ static void hid_reset(struct work_struct *work) dev_dbg(&usbhid->intf->dev, "resetting device\n"); rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); if (rc_lock >= 0) { - rc = usb_reset_composite_device(hid_to_usb_dev(hid)); + rc = usb_reset_device(hid_to_usb_dev(hid)); if (rc_lock) usb_unlock_device(hid_to_usb_dev(hid)); } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index bc1cce5cf758..e09935acae80 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -872,7 +872,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return usb_reset_composite_device(ps->dev); + return usb_reset_device(ps->dev); } static int proc_setintf(struct dev_state *ps, void __user *arg) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 207c33d369be..bb3ecc4c08f2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -135,6 +135,8 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); #define HUB_DEBOUNCE_STABLE 100 +static int usb_reset_and_verify_device(struct usb_device *udev); + static inline char *portspeed(int portstatus) { if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) @@ -1971,7 +1973,7 @@ static int finish_port_resume(struct usb_device *udev) * resumed. */ if (udev->reset_resume) - status = usb_reset_device(udev); + status = usb_reset_and_verify_device(udev); /* 10.5.4.5 says be sure devices in the tree are still there. * For now let's assume the device didn't go crazy on resume, @@ -2030,7 +2032,7 @@ static int finish_port_resume(struct usb_device *udev) * to it will be lost. Using the USB_PERSIST facility, the device can be * made to appear as if it had not disconnected. * - * This facility can be dangerous. Although usb_reset_device() makes + * This facility can be dangerous. Although usb_reset_and_verify_device() makes * every effort to insure that the same device is present after the * reset as before, it cannot provide a 100% guarantee. Furthermore it's * quite possible for a device to remain unaltered but its media to be @@ -2140,7 +2142,7 @@ int usb_port_resume(struct usb_device *udev) hub_port_logical_disconnect(hub, port1); } else if (udev->reset_resume) { dev_dbg(&udev->dev, "reset-resume\n"); - status = usb_reset_device(udev); + status = usb_reset_and_verify_device(udev); } return status; } @@ -2321,7 +2323,7 @@ static int hub_set_address(struct usb_device *udev, int devnum) * Returns device in USB_STATE_ADDRESS, except on error. * * If this is called for an already-existing device (as part of - * usb_reset_device), the caller must own the device lock. For a + * usb_reset_and_verify_device), the caller must own the device lock. For a * newly detected device that is not accessible through any global * pointers, it's not necessary to lock the device. */ @@ -2638,7 +2640,7 @@ hub_power_remaining (struct usb_hub *hub) * This routine is called when: * a port connection-change occurs; * a port enable-change occurs (often caused by EMI); - * usb_reset_device() encounters changed descriptors (as from + * usb_reset_and_verify_device() encounters changed descriptors (as from * a firmware download) * caller already locked the hub */ @@ -2712,7 +2714,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, #endif } else { - status = usb_reset_composite_device(udev); + status = usb_reset_device(udev); } usb_unlock_device(udev); @@ -2940,7 +2942,7 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - ret = usb_reset_composite_device(hdev); + ret = usb_reset_device(hdev); if (ret) { dev_dbg (hub_dev, "error resetting hub: %d\n", ret); @@ -3233,12 +3235,12 @@ static int descriptors_changed(struct usb_device *udev, } /** - * usb_reset_device - perform a USB port reset to reinitialize a device + * usb_reset_and_verify_device - perform a USB port reset to reinitialize a device * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * * WARNING - don't use this routine to reset a composite device * (one with multiple interfaces owned by separate drivers)! - * Use usb_reset_composite_device() instead. + * Use usb_reset_device() instead. * * Do a port reset, reassign the device's address, and establish its * former operating configuration. If the reset fails, or the device's @@ -3262,7 +3264,7 @@ static int descriptors_changed(struct usb_device *udev, * holding the device lock because these tasks should always call * usb_autopm_resume_device(), thereby preventing any unwanted autoresume. */ -int usb_reset_device(struct usb_device *udev) +static int usb_reset_and_verify_device(struct usb_device *udev) { struct usb_device *parent_hdev = udev->parent; struct usb_hub *parent_hub; @@ -3350,24 +3352,23 @@ re_enumerate: hub_port_logical_disconnect(parent_hub, port1); return -ENODEV; } -EXPORT_SYMBOL_GPL(usb_reset_device); /** - * usb_reset_composite_device - warn interface drivers and perform a USB port reset + * usb_reset_device - warn interface drivers and perform a USB port reset * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * * Warns all drivers bound to registered interfaces (using their pre_reset * method), performs the port reset, and then lets the drivers know that * the reset is over (using their post_reset method). * - * Return value is the same as for usb_reset_device(). + * Return value is the same as for usb_reset_and_verify_device(). * * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). */ -int usb_reset_composite_device(struct usb_device *udev) +int usb_reset_device(struct usb_device *udev) { int ret; int i; @@ -3397,7 +3398,7 @@ int usb_reset_composite_device(struct usb_device *udev) } } - ret = usb_reset_device(udev); + ret = usb_reset_and_verify_device(udev); if (config) { for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { @@ -3416,4 +3417,4 @@ int usb_reset_composite_device(struct usb_device *udev) usb_autosuspend_device(udev); return ret; } -EXPORT_SYMBOL_GPL(usb_reset_composite_device); +EXPORT_SYMBOL_GPL(usb_reset_device); diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 670e4cbd1f06..fcbbfdb7b2b0 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1172,8 +1172,8 @@ int usb_stor_port_reset(struct us_data *us) result = -EIO; US_DEBUGP("No reset during disconnect\n"); } else { - result = usb_reset_composite_device(us->pusb_dev); - US_DEBUGP("usb_reset_composite_device returns %d\n", + result = usb_reset_device(us->pusb_dev); + US_DEBUGP("usb_reset_device returns %d\n", result); } if (rc_lock) diff --git a/include/linux/usb.h b/include/linux/usb.h index c74cc64bddc8..3cc8db5254d1 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -497,7 +497,6 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, /* USB port reset for device reinitialization */ extern int usb_reset_device(struct usb_device *dev); -extern int usb_reset_composite_device(struct usb_device *dev); extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); @@ -957,9 +956,9 @@ struct usbdrv_wrap { * @resume: Called when the device is being resumed by the system. * @reset_resume: Called when the suspended device has been reset instead * of being resumed. - * @pre_reset: Called by usb_reset_composite_device() when the device + * @pre_reset: Called by usb_reset_device() when the device * is about to be reset. - * @post_reset: Called by usb_reset_composite_device() after the device + * @post_reset: Called by usb_reset_device() after the device * has been reset * @id_table: USB drivers use ID table to support hotplugging. * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set -- cgit From 78d9a487ee961c356e1a934d9a92eca38ffb3a70 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 23 Jun 2008 16:00:40 -0400 Subject: USB: Force unbinding of drivers lacking reset_resume or other methods This patch (as1024) takes care of a FIXME issue: Drivers that don't have the necessary suspend, resume, reset_resume, pre_reset, or post_reset methods will be unbound and their interface reprobed when one of the unsupported events occurs. This is made slightly more difficult by the fact that bind operations won't work during a system sleep transition. So instead the code has to defer the operation until the transition ends. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 131 ++++++++++++++++++++++++++++++++++++++++------ drivers/usb/core/hub.c | 27 +++++++--- drivers/usb/core/usb.h | 2 + include/linux/usb.h | 1 + 4 files changed, 140 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 8da1a56659be..ddb54e14a5c5 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -201,6 +201,7 @@ static int usb_probe_interface(struct device *dev) intf = to_usb_interface(dev); udev = interface_to_usbdev(intf); + intf->needs_binding = 0; if (udev->authorized == 0) { dev_err(&intf->dev, "Device is not authorized for usage\n"); @@ -311,6 +312,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, dev->driver = &driver->drvwrap.driver; usb_set_intfdata(iface, priv); + iface->needs_binding = 0; usb_pm_lock(udev); iface->condition = USB_INTERFACE_BOUND; @@ -772,6 +774,104 @@ void usb_deregister(struct usb_driver *driver) } EXPORT_SYMBOL_GPL(usb_deregister); + +/* Forced unbinding of a USB interface driver, either because + * it doesn't support pre_reset/post_reset/reset_resume or + * because it doesn't support suspend/resume. + * + * The caller must hold @intf's device's lock, but not its pm_mutex + * and not @intf->dev.sem. + */ +void usb_forced_unbind_intf(struct usb_interface *intf) +{ + struct usb_driver *driver = to_usb_driver(intf->dev.driver); + + dev_dbg(&intf->dev, "forced unbind\n"); + usb_driver_release_interface(driver, intf); + + /* Mark the interface for later rebinding */ + intf->needs_binding = 1; +} + +/* Delayed forced unbinding of a USB interface driver and scan + * for rebinding. + * + * The caller must hold @intf's device's lock, but not its pm_mutex + * and not @intf->dev.sem. + * + * FIXME: The caller must block system sleep transitions. + */ +void usb_rebind_intf(struct usb_interface *intf) +{ + int rc; + + /* Delayed unbind of an existing driver */ + if (intf->dev.driver) { + struct usb_driver *driver = + to_usb_driver(intf->dev.driver); + + dev_dbg(&intf->dev, "forced unbind\n"); + usb_driver_release_interface(driver, intf); + } + + /* Try to rebind the interface */ + intf->needs_binding = 0; + rc = device_attach(&intf->dev); + if (rc < 0) + dev_warn(&intf->dev, "rebind failed: %d\n", rc); +} + +#define DO_UNBIND 0 +#define DO_REBIND 1 + +/* Unbind drivers for @udev's interfaces that don't support suspend/resume, + * or rebind interfaces that have been unbound, according to @action. + * + * The caller must hold @udev's device lock. + * FIXME: For rebinds, the caller must block system sleep transitions. + */ +static void do_unbind_rebind(struct usb_device *udev, int action) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + struct usb_driver *drv; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + switch (action) { + case DO_UNBIND: + if (intf->dev.driver) { + drv = to_usb_driver(intf->dev.driver); + if (!drv->suspend || !drv->resume) + usb_forced_unbind_intf(intf); + } + break; + case DO_REBIND: + if (intf->needs_binding) { + + /* FIXME: The next line is needed because we are going to probe + * the interface, but as far as the PM core is concerned the + * interface is still suspended. The problem wouldn't exist + * if we could rebind the interface during the interface's own + * resume() call, but at the time the usb_device isn't locked! + * + * The real solution will be to carry this out during the device's + * complete() callback. Until that is implemented, we have to + * use this hack. + */ +// intf->dev.power.sleeping = 0; + + usb_rebind_intf(intf); + } + break; + } + } + } +} + #ifdef CONFIG_PM /* Caller has locked udev's pm_mutex */ @@ -841,7 +941,7 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) goto done; driver = to_usb_driver(intf->dev.driver); - if (driver->suspend && driver->resume) { + if (driver->suspend) { status = driver->suspend(intf, msg); if (status == 0) mark_quiesced(intf); @@ -849,12 +949,10 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) dev_err(&intf->dev, "%s error %d\n", "suspend", status); } else { - /* - * FIXME else if there's no suspend method, disconnect... - * Not possible if auto_pm is set... - */ - dev_warn(&intf->dev, "no suspend for driver %s?\n", - driver->name); + /* Later we will unbind the driver and reprobe */ + intf->needs_binding = 1; + dev_warn(&intf->dev, "no %s for driver %s?\n", + "suspend", driver->name); mark_quiesced(intf); } @@ -878,10 +976,12 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) goto done; /* Can't resume it if it doesn't have a driver. */ - if (intf->condition == USB_INTERFACE_UNBOUND) { - status = -ENOTCONN; + if (intf->condition == USB_INTERFACE_UNBOUND) + goto done; + + /* Don't resume if the interface is marked for rebinding */ + if (intf->needs_binding) goto done; - } driver = to_usb_driver(intf->dev.driver); if (reset_resume) { @@ -891,7 +991,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) dev_err(&intf->dev, "%s error %d\n", "reset_resume", status); } else { - /* status = -EOPNOTSUPP; */ + intf->needs_binding = 1; dev_warn(&intf->dev, "no %s for driver %s?\n", "reset_resume", driver->name); } @@ -902,7 +1002,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) dev_err(&intf->dev, "%s error %d\n", "resume", status); } else { - /* status = -EOPNOTSUPP; */ + intf->needs_binding = 1; dev_warn(&intf->dev, "no %s for driver %s?\n", "resume", driver->name); } @@ -910,11 +1010,10 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) done: dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status); - if (status == 0) + if (status == 0 && intf->condition == USB_INTERFACE_BOUND) mark_active(intf); - /* FIXME: Unbind the driver and reprobe if the resume failed - * (not possible if auto_pm is set) */ + /* Later we will unbind the driver and/or reprobe, if necessary */ return status; } @@ -1470,6 +1569,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) { int status; + do_unbind_rebind(udev, DO_UNBIND); usb_pm_lock(udev); udev->auto_pm = 0; status = usb_suspend_both(udev, msg); @@ -1497,6 +1597,7 @@ int usb_external_resume_device(struct usb_device *udev) status = usb_resume_both(udev); udev->last_busy = jiffies; usb_pm_unlock(udev); + do_unbind_rebind(udev, DO_REBIND); /* Now that the device is awake, we can start trying to autosuspend * it again. */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index bb3ecc4c08f2..f1efabbc1ca2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3367,6 +3367,11 @@ re_enumerate: * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). + * + * If an interface is currently being probed or disconnected, we assume + * its driver knows how to handle resets. For all other interfaces, + * if the driver doesn't have pre_reset and post_reset methods then + * we attempt to unbind it and rebind afterward. */ int usb_reset_device(struct usb_device *udev) { @@ -3388,12 +3393,17 @@ int usb_reset_device(struct usb_device *udev) for (i = 0; i < config->desc.bNumInterfaces; ++i) { struct usb_interface *cintf = config->interface[i]; struct usb_driver *drv; + int unbind = 0; if (cintf->dev.driver) { drv = to_usb_driver(cintf->dev.driver); - if (drv->pre_reset) - (drv->pre_reset)(cintf); - /* FIXME: Unbind if pre_reset returns an error or isn't defined */ + if (drv->pre_reset && drv->post_reset) + unbind = (drv->pre_reset)(cintf); + else if (cintf->condition == + USB_INTERFACE_BOUND) + unbind = 1; + if (unbind) + usb_forced_unbind_intf(cintf); } } } @@ -3404,13 +3414,18 @@ int usb_reset_device(struct usb_device *udev) for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { struct usb_interface *cintf = config->interface[i]; struct usb_driver *drv; + int rebind = cintf->needs_binding; - if (cintf->dev.driver) { + if (!rebind && cintf->dev.driver) { drv = to_usb_driver(cintf->dev.driver); if (drv->post_reset) - (drv->post_reset)(cintf); - /* FIXME: Unbind if post_reset returns an error or isn't defined */ + rebind = (drv->post_reset)(cintf); + else if (cintf->condition == + USB_INTERFACE_BOUND) + rebind = 1; } + if (rebind) + usb_rebind_intf(cintf); } } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1a8bc21c335e..d3eb0a29bca1 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -29,6 +29,8 @@ extern int usb_choose_configuration(struct usb_device *udev); extern void usb_kick_khubd(struct usb_device *dev); extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); +extern void usb_forced_unbind_intf(struct usb_interface *intf); +extern void usb_rebind_intf(struct usb_interface *intf); extern int usb_hub_init(void); extern void usb_hub_cleanup(void); diff --git a/include/linux/usb.h b/include/linux/usb.h index 3cc8db5254d1..5811c5da69f9 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -160,6 +160,7 @@ struct usb_interface { unsigned is_active:1; /* the interface is not suspended */ unsigned sysfs_files_created:1; /* the sysfs attributes exist */ unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ + unsigned needs_binding:1; /* needs delayed unbind/rebind */ struct device dev; /* interface specific device info */ struct device *usb_dev; -- cgit From 18ad7a61e1b700dfe177fabf1c350b4f5d4da8ac Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Fri, 30 May 2008 22:51:33 +1000 Subject: of_gpio: Should use new header Since commit 7560fa60fcdcdb0da662f6a9fad9064b554ef46c (gpio: and "no GPIO support here" stubs) drivers can use GPIOs if they're available, but don't require them. This patch actually enables this feature. Signed-off-by: Wolfgang Grandegger Signed-off-by: Anton Vorontsov Signed-off-by: Benjamin Herrenschmidt --- include/linux/of_gpio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 2ee97e9877a7..67db101d0eb8 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -15,7 +15,7 @@ #define __LINUX_OF_GPIO_H #include -#include +#include #ifdef CONFIG_OF_GPIO -- cgit From 1ed6af73440c5ec920884bb800685a8cab4ce847 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Fri, 18 Jul 2008 23:03:34 +1000 Subject: powerpc/cell: Add DMA_ATTR_WEAK_ORDERING dma attribute and use in Cell IOMMU code Introduce a new dma attriblue DMA_ATTR_WEAK_ORDERING to use weak ordering on DMA mappings in the Cell processor. Add the code to the Cell's IOMMU implementation to use this code. Dynamic mappings can be weakly or strongly ordered on an individual basis but the fixed mapping has to be either completely strong or completely weak. This is currently decided by a kernel boot option (pass iommu_fixed=weak for a weakly ordered fixed linear mapping, strongly ordered is the default). Signed-off-by: Mark Nelson Signed-off-by: Arnd Bergmann Signed-off-by: Benjamin Herrenschmidt --- Documentation/DMA-attributes.txt | 9 +++ arch/powerpc/platforms/cell/iommu.c | 113 ++++++++++++++++++++++++++++++++++-- include/linux/dma-attrs.h | 1 + 3 files changed, 118 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/Documentation/DMA-attributes.txt b/Documentation/DMA-attributes.txt index 6d772f84b477..b768cc0e402b 100644 --- a/Documentation/DMA-attributes.txt +++ b/Documentation/DMA-attributes.txt @@ -22,3 +22,12 @@ ready and available in memory. The DMA of the "completion indication" could race with data DMA. Mapping the memory used for completion indications with DMA_ATTR_WRITE_BARRIER would prevent the race. +DMA_ATTR_WEAK_ORDERING +---------------------- + +DMA_ATTR_WEAK_ORDERING specifies that reads and writes to the mapping +may be weakly ordered, that is that reads and writes may pass each other. + +Since it is optional for platforms to implement DMA_ATTR_WEAK_ORDERING, +those that do not will simply ignore the attribute and exhibit default +behavior. diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 3b7078453e7f..208005ca262c 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -199,6 +199,8 @@ static void tce_build_cell(struct iommu_table *tbl, long index, long npages, base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW | (window->ioid & IOPTE_IOID_Mask); #endif + if (unlikely(dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))) + base_pte &= ~IOPTE_SO_RW; io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset); @@ -539,7 +541,9 @@ static struct cbe_iommu *cell_iommu_for_node(int nid) static unsigned long cell_dma_direct_offset; static unsigned long dma_iommu_fixed_base; -struct dma_mapping_ops dma_iommu_fixed_ops; + +/* iommu_fixed_is_weak is set if booted with iommu_fixed=weak */ +static int iommu_fixed_is_weak; static struct iommu_table *cell_get_iommu_table(struct device *dev) { @@ -563,6 +567,98 @@ static struct iommu_table *cell_get_iommu_table(struct device *dev) return &window->table; } +/* A coherent allocation implies strong ordering */ + +static void *dma_fixed_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + if (iommu_fixed_is_weak) + return iommu_alloc_coherent(dev, cell_get_iommu_table(dev), + size, dma_handle, + device_to_mask(dev), flag, + dev->archdata.numa_node); + else + return dma_direct_ops.alloc_coherent(dev, size, dma_handle, + flag); +} + +static void dma_fixed_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + if (iommu_fixed_is_weak) + iommu_free_coherent(cell_get_iommu_table(dev), size, vaddr, + dma_handle); + else + dma_direct_ops.free_coherent(dev, size, vaddr, dma_handle); +} + +static dma_addr_t dma_fixed_map_single(struct device *dev, void *ptr, + size_t size, + enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + return dma_direct_ops.map_single(dev, ptr, size, direction, + attrs); + else + return iommu_map_single(dev, cell_get_iommu_table(dev), ptr, + size, device_to_mask(dev), direction, + attrs); +} + +static void dma_fixed_unmap_single(struct device *dev, dma_addr_t dma_addr, + size_t size, + enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + dma_direct_ops.unmap_single(dev, dma_addr, size, direction, + attrs); + else + iommu_unmap_single(cell_get_iommu_table(dev), dma_addr, size, + direction, attrs); +} + +static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs); + else + return iommu_map_sg(dev, cell_get_iommu_table(dev), sg, nents, + device_to_mask(dev), direction, attrs); +} + +static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)) + dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs); + else + iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents, direction, + attrs); +} + +static int dma_fixed_dma_supported(struct device *dev, u64 mask) +{ + return mask == DMA_64BIT_MASK; +} + +static int dma_set_mask_and_switch(struct device *dev, u64 dma_mask); + +struct dma_mapping_ops dma_iommu_fixed_ops = { + .alloc_coherent = dma_fixed_alloc_coherent, + .free_coherent = dma_fixed_free_coherent, + .map_single = dma_fixed_map_single, + .unmap_single = dma_fixed_unmap_single, + .map_sg = dma_fixed_map_sg, + .unmap_sg = dma_fixed_unmap_sg, + .dma_supported = dma_fixed_dma_supported, + .set_dma_mask = dma_set_mask_and_switch, +}; + static void cell_dma_dev_setup_fixed(struct device *dev); static void cell_dma_dev_setup(struct device *dev) @@ -919,9 +1015,16 @@ static void cell_iommu_setup_fixed_ptab(struct cbe_iommu *iommu, pr_debug("iommu: mapping 0x%lx pages from 0x%lx\n", fsize, fbase); - base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | IOPTE_SO_RW + base_pte = IOPTE_PP_W | IOPTE_PP_R | IOPTE_M | (cell_iommu_get_ioid(np) & IOPTE_IOID_Mask); + if (iommu_fixed_is_weak) + pr_info("IOMMU: Using weak ordering for fixed mapping\n"); + else { + pr_info("IOMMU: Using strong ordering for fixed mapping\n"); + base_pte |= IOPTE_SO_RW; + } + for (uaddr = 0; uaddr < fsize; uaddr += (1 << 24)) { /* Don't touch the dynamic region */ ioaddr = uaddr + fbase; @@ -1037,9 +1140,6 @@ static int __init cell_iommu_fixed_mapping_init(void) cell_iommu_setup_window(iommu, np, dbase, dsize, 0); } - dma_iommu_fixed_ops = dma_direct_ops; - dma_iommu_fixed_ops.set_dma_mask = dma_set_mask_and_switch; - dma_iommu_ops.set_dma_mask = dma_set_mask_and_switch; set_pci_dma_ops(&dma_iommu_ops); @@ -1053,6 +1153,9 @@ static int __init setup_iommu_fixed(char *str) if (strcmp(str, "off") == 0) iommu_fixed_disabled = 1; + else if (strcmp(str, "weak") == 0) + iommu_fixed_is_weak = 1; + return 1; } __setup("iommu_fixed=", setup_iommu_fixed); diff --git a/include/linux/dma-attrs.h b/include/linux/dma-attrs.h index 1677e2bfa00c..71ad34eca6e3 100644 --- a/include/linux/dma-attrs.h +++ b/include/linux/dma-attrs.h @@ -12,6 +12,7 @@ */ enum dma_attr { DMA_ATTR_WRITE_BARRIER, + DMA_ATTR_WEAK_ORDERING, DMA_ATTR_MAX, }; -- cgit From e105b8bfc769b0545b6f0f395179d1e43cbee822 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 21 Apr 2008 10:51:07 -0700 Subject: sysfs: add /sys/dev/{char,block} to lookup sysfs path by major:minor Why?: There are occasions where userspace would like to access sysfs attributes for a device but it may not know how sysfs has named the device or the path. For example what is the sysfs path for /dev/disk/by-id/ata-ST3160827AS_5MT004CK? With this change a call to stat(2) returns the major:minor then userspace can see that /sys/dev/block/8:32 links to /sys/block/sdc. What are the alternatives?: 1/ Add an ioctl to return the path: Doable, but sysfs is meant to reduce the need to proliferate ioctl interfaces into the kernel, so this seems counter productive. 2/ Use udev to create these symlinks: Also doable, but it adds a udev dependency to utilities that might be running in a limited environment like an initramfs. 3/ Do a full-tree search of sysfs. [kay.sievers@vrfy.org: fix duplicate registrations] [kay.sievers@vrfy.org: cleanup suggestions] Cc: Neil Brown Cc: Tejun Heo Acked-by: Kay Sievers Reviewed-by: SL Baur Acked-by: Kay Sievers Acked-by: Mark Lord Acked-by: H. Peter Anvin Signed-off-by: Dan Williams Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-dev | 20 +++++++++ Documentation/filesystems/sysfs.txt | 6 +++ block/genhd.c | 5 ++- drivers/base/class.c | 4 ++ drivers/base/core.c | 83 ++++++++++++++++++++++++++++++++++++- drivers/usb/core/devio.c | 5 +++ include/linux/device.h | 3 ++ 7 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-dev (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-dev b/Documentation/ABI/testing/sysfs-dev new file mode 100644 index 000000000000..a9f2b8b0530f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-dev @@ -0,0 +1,20 @@ +What: /sys/dev +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Dan Williams +Description: The /sys/dev tree provides a method to look up the sysfs + path for a device using the information returned from + stat(2). There are two directories, 'block' and 'char', + beneath /sys/dev containing symbolic links with names of + the form ":". These links point to the + corresponding sysfs path for the given device. + + Example: + $ readlink /sys/dev/block/8:32 + ../../block/sdc + + Entries in /sys/dev/char and /sys/dev/block will be + dynamically created and destroyed as devices enter and + leave the system. + +Users: mdadm diff --git a/Documentation/filesystems/sysfs.txt b/Documentation/filesystems/sysfs.txt index 7f27b8f840d0..9e9c348275a9 100644 --- a/Documentation/filesystems/sysfs.txt +++ b/Documentation/filesystems/sysfs.txt @@ -248,6 +248,7 @@ The top level sysfs directory looks like: block/ bus/ class/ +dev/ devices/ firmware/ net/ @@ -274,6 +275,11 @@ fs/ contains a directory for some filesystems. Currently each filesystem wanting to export attributes must create its own hierarchy below fs/ (see ./fuse.txt for an example). +dev/ contains two directories char/ and block/. Inside these two +directories there are symlinks named :. These symlinks +point to the sysfs directory for the given device. /sys/dev provides a +quick way to lookup the sysfs interface for a device from the result of +a stat(2) operation. More information can driver-model specific features can be found in Documentation/driver-model/. diff --git a/block/genhd.c b/block/genhd.c index 9074f384b097..24e3fc9095fe 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -370,7 +370,10 @@ static struct kobject *base_probe(dev_t devt, int *part, void *data) static int __init genhd_device_init(void) { - int error = class_register(&block_class); + int error; + + block_class.dev_kobj = sysfs_dev_block_kobj; + error = class_register(&block_class); if (unlikely(error)) return error; bdev_map = kobj_map_init(base_probe, &block_class_lock); diff --git a/drivers/base/class.c b/drivers/base/class.c index e085af0ff94f..71ce3ff6bdf5 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -148,6 +148,10 @@ int class_register(struct class *cls) if (error) return error; + /* set the default /sys/dev directory for devices of this class */ + if (!cls->dev_kobj) + cls->dev_kobj = sysfs_dev_char_kobj; + #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK) /* let the block class directory show up in the root of sysfs */ if (cls != &block_class) diff --git a/drivers/base/core.c b/drivers/base/core.c index ee0a51a3a41d..be9aba4dc2fb 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -27,6 +27,9 @@ int (*platform_notify)(struct device *dev) = NULL; int (*platform_notify_remove)(struct device *dev) = NULL; +static struct kobject *dev_kobj; +struct kobject *sysfs_dev_char_kobj; +struct kobject *sysfs_dev_block_kobj; #ifdef CONFIG_BLOCK static inline int device_is_not_partition(struct device *dev) @@ -775,6 +778,54 @@ int dev_set_name(struct device *dev, const char *fmt, ...) } EXPORT_SYMBOL_GPL(dev_set_name); +/** + * device_to_dev_kobj - select a /sys/dev/ directory for the device + * @dev: device + * + * By default we select char/ for new entries. Setting class->dev_obj + * to NULL prevents an entry from being created. class->dev_kobj must + * be set (or cleared) before any devices are registered to the class + * otherwise device_create_sys_dev_entry() and + * device_remove_sys_dev_entry() will disagree about the the presence + * of the link. + */ +static struct kobject *device_to_dev_kobj(struct device *dev) +{ + struct kobject *kobj; + + if (dev->class) + kobj = dev->class->dev_kobj; + else + kobj = sysfs_dev_char_kobj; + + return kobj; +} + +static int device_create_sys_dev_entry(struct device *dev) +{ + struct kobject *kobj = device_to_dev_kobj(dev); + int error = 0; + char devt_str[15]; + + if (kobj) { + format_dev_t(devt_str, dev->devt); + error = sysfs_create_link(kobj, &dev->kobj, devt_str); + } + + return error; +} + +static void device_remove_sys_dev_entry(struct device *dev) +{ + struct kobject *kobj = device_to_dev_kobj(dev); + char devt_str[15]; + + if (kobj) { + format_dev_t(devt_str, dev->devt); + sysfs_remove_link(kobj, devt_str); + } +} + /** * device_add - add device to device hierarchy. * @dev: device. @@ -829,6 +880,10 @@ int device_add(struct device *dev) error = device_create_file(dev, &devt_attr); if (error) goto ueventattrError; + + error = device_create_sys_dev_entry(dev); + if (error) + goto devtattrError; } error = device_add_class_symlinks(dev); @@ -872,6 +927,9 @@ int device_add(struct device *dev) AttrsError: device_remove_class_symlinks(dev); SymlinkError: + if (MAJOR(dev->devt)) + device_remove_sys_dev_entry(dev); + devtattrError: if (MAJOR(dev->devt)) device_remove_file(dev, &devt_attr); ueventattrError: @@ -948,8 +1006,10 @@ void device_del(struct device *dev) device_pm_remove(dev); if (parent) klist_del(&dev->knode_parent); - if (MAJOR(dev->devt)) + if (MAJOR(dev->devt)) { + device_remove_sys_dev_entry(dev); device_remove_file(dev, &devt_attr); + } if (dev->class) { device_remove_class_symlinks(dev); @@ -1074,7 +1134,25 @@ int __init devices_init(void) devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); if (!devices_kset) return -ENOMEM; + dev_kobj = kobject_create_and_add("dev", NULL); + if (!dev_kobj) + goto dev_kobj_err; + sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); + if (!sysfs_dev_block_kobj) + goto block_kobj_err; + sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); + if (!sysfs_dev_char_kobj) + goto char_kobj_err; + return 0; + + char_kobj_err: + kobject_put(sysfs_dev_block_kobj); + block_kobj_err: + kobject_put(dev_kobj); + dev_kobj_err: + kset_unregister(devices_kset); + return -ENOMEM; } EXPORT_SYMBOL_GPL(device_for_each_child); @@ -1447,4 +1525,7 @@ void device_shutdown(void) dev->driver->shutdown(dev); } } + kobject_put(sysfs_dev_char_kobj); + kobject_put(sysfs_dev_block_kobj); + kobject_put(dev_kobj); } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 54a350ccd033..6fbc8f5ab80c 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1792,6 +1792,11 @@ int __init usb_devio_init(void) usb_classdev_class = NULL; goto out; } + /* devices of this class shadow the major:minor of their parent + * device, so clear ->dev_kobj to prevent adding duplicate entries + * to /sys/dev + */ + usb_classdev_class->dev_kobj = NULL; usb_register_notify(&usbdev_nb); #endif diff --git a/include/linux/device.h b/include/linux/device.h index f71a78d123ae..e49aa74f248c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -193,6 +193,7 @@ struct class { struct semaphore sem; /* locks children, devices, interfaces */ struct class_attribute *class_attrs; struct device_attribute *dev_attrs; + struct kobject *dev_kobj; int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); @@ -205,6 +206,8 @@ struct class { struct pm_ops *pm; }; +extern struct kobject *sysfs_dev_block_kobj; +extern struct kobject *sysfs_dev_char_kobj; extern int __must_check class_register(struct class *class); extern void class_unregister(struct class *class); extern int class_for_each_device(struct class *class, void *data, -- cgit From ccea44fadca396b0f89aed5bdeb60e4abb212566 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 21 May 2008 12:52:33 -0700 Subject: driver core: remove device_create() There are no more users of this, and it is racy. Use device_create_drvdata() or device_create_vargs() instead. Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 34 ---------------------------------- include/linux/device.h | 3 --- 2 files changed, 37 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index be9aba4dc2fb..4dc0d272f6d9 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1273,40 +1273,6 @@ struct device *device_create_drvdata(struct class *class, } EXPORT_SYMBOL_GPL(device_create_drvdata); -/** - * device_create - creates a device and registers it with sysfs - * @class: pointer to the struct class that this device should be registered to - * @parent: pointer to the parent struct device of this new device, if any - * @devt: the dev_t for the char device to be added - * @fmt: string for the device's name - * - * This function can be used by char device classes. A struct device - * will be created in sysfs, registered to the specified class. - * - * A "dev" file will be created, showing the dev_t for the device, if - * the dev_t is not 0,0. - * If a pointer to a parent struct device is passed in, the newly created - * struct device will be a child of that device in sysfs. - * The pointer to the struct device will be returned from the call. - * Any further sysfs files that might be required can be created using this - * pointer. - * - * Note: the struct class passed to this function must have previously - * been created with a call to class_create(). - */ -struct device *device_create(struct class *class, struct device *parent, - dev_t devt, const char *fmt, ...) -{ - va_list vargs; - struct device *dev; - - va_start(vargs, fmt); - dev = device_create_vargs(class, parent, devt, NULL, fmt, vargs); - va_end(vargs); - return dev; -} -EXPORT_SYMBOL_GPL(device_create); - static int __match_devt(struct device *dev, void *data) { dev_t *devt = data; diff --git a/include/linux/device.h b/include/linux/device.h index e49aa74f248c..a3ef5a2d4fbb 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -470,9 +470,6 @@ extern struct device *device_create_vargs(struct class *cls, void *drvdata, const char *fmt, va_list vargs); -extern struct device *device_create(struct class *cls, struct device *parent, - dev_t devt, const char *fmt, ...) - __attribute__((format(printf, 4, 5))); extern struct device *device_create_drvdata(struct class *cls, struct device *parent, dev_t devt, -- cgit From 4e10673944a5c386378ff9d692ae37e19993f9d5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 21 Jul 2008 20:03:34 -0700 Subject: device create: convert device_create_drvdata to device_create Now that device_create() has been audited, rename things back to the original call to be sane. Keep the device_create_drvdata macro around to make merges easier. Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 11 ++++------- include/linux/device.h | 9 ++++----- 2 files changed, 8 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index 4dc0d272f6d9..9ae28aa709d5 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1236,7 +1236,7 @@ error: EXPORT_SYMBOL_GPL(device_create_vargs); /** - * device_create_drvdata - creates a device and registers it with sysfs + * device_create - creates a device and registers it with sysfs * @class: pointer to the struct class that this device should be registered to * @parent: pointer to the parent struct device of this new device, if any * @devt: the dev_t for the char device to be added @@ -1257,11 +1257,8 @@ EXPORT_SYMBOL_GPL(device_create_vargs); * Note: the struct class passed to this function must have previously * been created with a call to class_create(). */ -struct device *device_create_drvdata(struct class *class, - struct device *parent, - dev_t devt, - void *drvdata, - const char *fmt, ...) +struct device *device_create(struct class *class, struct device *parent, + dev_t devt, void *drvdata, const char *fmt, ...) { va_list vargs; struct device *dev; @@ -1271,7 +1268,7 @@ struct device *device_create_drvdata(struct class *class, va_end(vargs); return dev; } -EXPORT_SYMBOL_GPL(device_create_drvdata); +EXPORT_SYMBOL_GPL(device_create); static int __match_devt(struct device *dev, void *data) { diff --git a/include/linux/device.h b/include/linux/device.h index a3ef5a2d4fbb..de178712e02c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -470,12 +470,11 @@ extern struct device *device_create_vargs(struct class *cls, void *drvdata, const char *fmt, va_list vargs); -extern struct device *device_create_drvdata(struct class *cls, - struct device *parent, - dev_t devt, - void *drvdata, - const char *fmt, ...) +extern struct device *device_create(struct class *cls, struct device *parent, + dev_t devt, void *drvdata, + const char *fmt, ...) __attribute__((format(printf, 5, 6))); +#define device_create_drvdata device_create extern void device_destroy(struct class *cls, dev_t devt); /* -- cgit From 93562b537659fc0f63920fd4d9d24f54e434f4c4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 May 2008 17:21:08 -0400 Subject: Driver Core: add ability for class_for_each_device to start in middle of list This mirrors the functionality that driver_for_each_device has as well. We add a start variable, and all callers of the function are fixed up at the same time. The block layer will be using this new functionality in a follow-on patch. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 21 +++++++++++++-------- drivers/i2c/i2c-core.c | 6 ++++-- drivers/ieee1394/nodemgr.c | 14 +++++++++----- drivers/power/apm_power.c | 2 +- drivers/power/power_supply_core.c | 4 ++-- include/linux/device.h | 3 ++- 6 files changed, 31 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index 71ce3ff6bdf5..2eb7048003a8 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -256,11 +256,14 @@ char *make_class_name(const char *name, struct kobject *kobj) /** * class_for_each_device - device iterator * @class: the class we're iterating + * @start: the device to start with in the list, if any. * @data: data for the callback * @fn: function to be called for each device * * Iterate over @class's list of devices, and call @fn for each, - * passing it @data. + * passing it @data. If @start is set, the list iteration will start + * there, otherwise if it is NULL, the iteration starts at the + * beginning of the list. * * We check the return of @fn each time. If it returns anything * other than 0, we break out and return that value. @@ -269,8 +272,8 @@ char *make_class_name(const char *name, struct kobject *kobj) * re-acquired in @fn, otherwise it will self-deadlocking. For * example, calls to add or remove class members would be verboten. */ -int class_for_each_device(struct class *class, void *data, - int (*fn)(struct device *, void *)) +int class_for_each_device(struct class *class, struct device *start, + void *data, int (*fn)(struct device *, void *)) { struct device *dev; int error = 0; @@ -279,12 +282,14 @@ int class_for_each_device(struct class *class, void *data, return -EINVAL; down(&class->sem); list_for_each_entry(dev, &class->devices, node) { + if (start) { + if (start == dev) + start = NULL; + continue; + } dev = get_device(dev); - if (dev) { - error = fn(dev, data); - put_device(dev); - } else - error = -ENODEV; + error = fn(dev, data); + put_device(dev); if (error) break; } diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 7608df83d6d1..7bf38c418086 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -722,7 +722,8 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) INIT_LIST_HEAD(&driver->clients); /* Walk the adapters that are already present */ - class_for_each_device(&i2c_adapter_class, driver, __attach_adapter); + class_for_each_device(&i2c_adapter_class, NULL, driver, + __attach_adapter); mutex_unlock(&core_lock); return 0; @@ -782,7 +783,8 @@ void i2c_del_driver(struct i2c_driver *driver) { mutex_lock(&core_lock); - class_for_each_device(&i2c_adapter_class, driver, __detach_adapter); + class_for_each_device(&i2c_adapter_class, NULL, driver, + __detach_adapter); driver_unregister(&driver->driver); pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name); diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index 05710c7c1220..47c0d85e0f32 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -1453,7 +1453,8 @@ static void nodemgr_suspend_ne(struct node_entry *ne) ne->in_limbo = 1; WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo)); - class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_driver_suspend); + class_for_each_device(&nodemgr_ud_class, NULL, ne, + __nodemgr_driver_suspend); } @@ -1462,7 +1463,8 @@ static void nodemgr_resume_ne(struct node_entry *ne) ne->in_limbo = 0; device_remove_file(&ne->device, &dev_attr_ne_in_limbo); - class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_driver_resume); + class_for_each_device(&nodemgr_ud_class, NULL, ne, + __nodemgr_driver_resume); HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]", NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid); } @@ -1498,7 +1500,8 @@ static int __nodemgr_update_pdrv(struct device *dev, void *data) static void nodemgr_update_pdrv(struct node_entry *ne) { - class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_update_pdrv); + class_for_each_device(&nodemgr_ud_class, NULL, ne, + __nodemgr_update_pdrv); } @@ -1591,7 +1594,8 @@ static void nodemgr_node_probe(struct host_info *hi, int generation) * while probes are time-consuming. (Well, those probes need some * improvement...) */ - class_for_each_device(&nodemgr_ne_class, ¶m, __nodemgr_node_probe); + class_for_each_device(&nodemgr_ne_class, NULL, ¶m, + __nodemgr_node_probe); /* If we had a bus reset while we were scanning the bus, it is * possible that we did not probe all nodes. In that case, we @@ -1826,7 +1830,7 @@ int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)) hip.cb = cb; hip.data = data; - error = class_for_each_device(&hpsb_host_class, &hip, + error = class_for_each_device(&hpsb_host_class, NULL, &hip, __nodemgr_for_each_host); return error; diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c index a4892275659d..936bae560fa1 100644 --- a/drivers/power/apm_power.c +++ b/drivers/power/apm_power.c @@ -78,7 +78,7 @@ static void find_main_battery(void) main_battery = NULL; bp.main = main_battery; - error = class_for_each_device(power_supply_class, &bp, + error = class_for_each_device(power_supply_class, NULL, &bp, __find_main_battery); if (error) { main_battery = bp.main; diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index af1633eb3b70..cb1ccb472921 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -41,7 +41,7 @@ static void power_supply_changed_work(struct work_struct *work) dev_dbg(psy->dev, "%s\n", __func__); - class_for_each_device(power_supply_class, psy, + class_for_each_device(power_supply_class, NULL, psy, __power_supply_changed_work); power_supply_update_leds(psy); @@ -79,7 +79,7 @@ int power_supply_am_i_supplied(struct power_supply *psy) { int error; - error = class_for_each_device(power_supply_class, psy, + error = class_for_each_device(power_supply_class, NULL, psy, __power_supply_am_i_supplied); dev_dbg(psy->dev, "%s %d\n", __func__, error); diff --git a/include/linux/device.h b/include/linux/device.h index de178712e02c..6d5b351b29c9 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -210,7 +210,8 @@ extern struct kobject *sysfs_dev_block_kobj; extern struct kobject *sysfs_dev_char_kobj; extern int __must_check class_register(struct class *class); extern void class_unregister(struct class *class); -extern int class_for_each_device(struct class *class, void *data, +extern int class_for_each_device(struct class *class, struct device *start, + void *data, int (*fn)(struct device *dev, void *data)); extern struct device *class_find_device(struct class *class, void *data, int (*match)(struct device *, void *)); -- cgit From 695794ae0c5bdd9bd06e35b118801e2e9be04f9e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 May 2008 17:21:08 -0400 Subject: Driver Core: add ability for class_find_device to start in middle of list This mirrors the functionality that driver_find_device has as well. We add a start variable, and all callers of the function are fixed up at the same time. The block layer will be using this new functionality in a follow-on patch. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 22 +++++++++++++--------- drivers/base/core.c | 2 +- drivers/ieee1394/nodemgr.c | 9 ++++++--- drivers/rtc/interface.c | 2 +- drivers/scsi/hosts.c | 3 ++- drivers/scsi/scsi_transport_iscsi.c | 4 ++-- drivers/spi/spi.c | 2 +- include/linux/device.h | 3 ++- 8 files changed, 28 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index 2eb7048003a8..3918d0e432d4 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -302,6 +302,7 @@ EXPORT_SYMBOL_GPL(class_for_each_device); /** * class_find_device - device iterator for locating a particular device * @class: the class we're iterating + * @start: Device to begin with * @data: data for the match function * @match: function to check device * @@ -319,8 +320,9 @@ EXPORT_SYMBOL_GPL(class_for_each_device); * re-acquired in @match, otherwise it will self-deadlocking. For * example, calls to add or remove class members would be verboten. */ -struct device *class_find_device(struct class *class, void *data, - int (*match)(struct device *, void *)) +struct device *class_find_device(struct class *class, struct device *start, + void *data, + int (*match)(struct device *, void *)) { struct device *dev; int found = 0; @@ -330,15 +332,17 @@ struct device *class_find_device(struct class *class, void *data, down(&class->sem); list_for_each_entry(dev, &class->devices, node) { + if (start) { + if (start == dev) + start = NULL; + continue; + } dev = get_device(dev); - if (dev) { - if (match(dev, data)) { - found = 1; - break; - } else - put_device(dev); - } else + if (match(dev, data)) { + found = 1; break; + } else + put_device(dev); } up(&class->sem); diff --git a/drivers/base/core.c b/drivers/base/core.c index 9ae28aa709d5..9f05de6f80b5 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1289,7 +1289,7 @@ void device_destroy(struct class *class, dev_t devt) { struct device *dev; - dev = class_find_device(class, &devt, __match_devt); + dev = class_find_device(class, NULL, &devt, __match_devt); if (dev) { put_device(dev); device_unregister(dev); diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index 47c0d85e0f32..994a21e5a0aa 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -754,7 +754,8 @@ static void nodemgr_remove_uds(struct node_entry *ne) */ mutex_lock(&nodemgr_serialize_remove_uds); for (;;) { - dev = class_find_device(&nodemgr_ud_class, ne, __match_ne); + dev = class_find_device(&nodemgr_ud_class, NULL, ne, + __match_ne); if (!dev) break; ud = container_of(dev, struct unit_directory, unit_dev); @@ -901,7 +902,8 @@ static struct node_entry *find_entry_by_guid(u64 guid) struct device *dev; struct node_entry *ne; - dev = class_find_device(&nodemgr_ne_class, &guid, __match_ne_guid); + dev = class_find_device(&nodemgr_ne_class, NULL, &guid, + __match_ne_guid); if (!dev) return NULL; ne = container_of(dev, struct node_entry, node_dev); @@ -940,7 +942,8 @@ static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, param.host = host; param.nodeid = nodeid; - dev = class_find_device(&nodemgr_ne_class, ¶m, __match_ne_nodeid); + dev = class_find_device(&nodemgr_ne_class, NULL, ¶m, + __match_ne_nodeid); if (!dev) return NULL; ne = container_of(dev, struct node_entry, node_dev); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 58b7336640ff..d397fa5f3a91 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -345,7 +345,7 @@ struct rtc_device *rtc_class_open(char *name) struct device *dev; struct rtc_device *rtc = NULL; - dev = class_find_device(rtc_class, name, __rtc_match); + dev = class_find_device(rtc_class, NULL, name, __rtc_match); if (dev) rtc = to_rtc_device(dev); diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 35cd892dce04..78dad28b70d5 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -466,7 +466,8 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) struct device *cdev; struct Scsi_Host *shost = ERR_PTR(-ENXIO); - cdev = class_find_device(&shost_class, &hostnum, __scsi_host_match); + cdev = class_find_device(&shost_class, NULL, &hostnum, + __scsi_host_match); if (cdev) { shost = scsi_host_get(class_to_shost(cdev)); put_device(cdev); diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 3af7cbcc5c5d..06748f318cd5 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -170,7 +170,7 @@ iscsi_create_endpoint(int dd_size) int err; for (id = 1; id < ISCSI_MAX_EPID; id++) { - dev = class_find_device(&iscsi_endpoint_class, &id, + dev = class_find_device(&iscsi_endpoint_class, NULL, &id, iscsi_match_epid); if (!dev) break; @@ -222,7 +222,7 @@ struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) struct iscsi_endpoint *ep; struct device *dev; - dev = class_find_device(&iscsi_endpoint_class, &handle, + dev = class_find_device(&iscsi_endpoint_class, NULL, &handle, iscsi_match_epid); if (!dev) return NULL; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1ad12afc6ba0..1771b2456bfa 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -502,7 +502,7 @@ struct spi_master *spi_busnum_to_master(u16 bus_num) struct device *dev; struct spi_master *master = NULL; - dev = class_find_device(&spi_master_class, &bus_num, + dev = class_find_device(&spi_master_class, NULL, &bus_num, __spi_master_match); if (dev) master = container_of(dev, struct spi_master, dev); diff --git a/include/linux/device.h b/include/linux/device.h index 6d5b351b29c9..c1f72984875f 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -213,7 +213,8 @@ extern void class_unregister(struct class *class); extern int class_for_each_device(struct class *class, struct device *start, void *data, int (*fn)(struct device *dev, void *data)); -extern struct device *class_find_device(struct class *class, void *data, +extern struct device *class_find_device(struct class *class, + struct device *start, void *data, int (*match)(struct device *, void *)); struct class_attribute { -- cgit From 7c71448b8aa80123fc521563d5f7c63a099d97ab Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Jan 2008 18:17:41 -0500 Subject: class: move driver core specific parts to a private structure This moves the portions of struct class that are dynamic (kobject and lock and lists) out of the main structure and into a dynamic, private, structure. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 27 ++++++++++++++++ drivers/base/class.c | 87 ++++++++++++++++++++++++++++---------------------- drivers/base/core.c | 57 +++++++++++++++++---------------- include/linux/device.h | 7 ++-- 4 files changed, 107 insertions(+), 71 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/base.h b/drivers/base/base.h index 2c9ae43e2219..0ec372a67762 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -36,6 +36,33 @@ struct driver_private { }; #define to_driver(obj) container_of(obj, struct driver_private, kobj) + +/** + * struct class_private - structure to hold the private to the driver core portions of the class structure. + * + * @subsys - the struct kset that defines this class. This is the main kobject + * @children - list of class_devices associated with this class + * @devices - list of devices associated with this class + * @interfaces - list of class_interfaces associated with this class + * @class_dirs - + * @sem - semaphore to protect the children, devices, and interfaces lists. + * @class - pointer back to the struct class that this structure is associated + * with. + * + * This structure is the one that is the actual kobject allowing struct + * class to be statically allocated safely. Nothing outside of the driver + * core should ever touch these fields. + */ +struct class_private { + struct kset subsys; + struct list_head devices; + struct list_head interfaces; + struct kset class_dirs; + struct semaphore sem; + struct class *class; +}; +#define to_class(obj) container_of(obj, struct class_private, subsys.kobj) + /* initialisation functions */ extern int devices_init(void); extern int buses_init(void); diff --git a/drivers/base/class.c b/drivers/base/class.c index 3918d0e432d4..06f09c929a91 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -21,17 +21,16 @@ #include "base.h" #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) -#define to_class(obj) container_of(obj, struct class, subsys.kobj) static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct class_attribute *class_attr = to_class_attr(attr); - struct class *dc = to_class(kobj); + struct class_private *cp = to_class(kobj); ssize_t ret = -EIO; if (class_attr->show) - ret = class_attr->show(dc, buf); + ret = class_attr->show(cp->class, buf); return ret; } @@ -39,17 +38,18 @@ static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct class_attribute *class_attr = to_class_attr(attr); - struct class *dc = to_class(kobj); + struct class_private *cp = to_class(kobj); ssize_t ret = -EIO; if (class_attr->store) - ret = class_attr->store(dc, buf, count); + ret = class_attr->store(cp->class, buf, count); return ret; } static void class_release(struct kobject *kobj) { - struct class *class = to_class(kobj); + struct class_private *cp = to_class(kobj); + struct class *class = cp->class; pr_debug("class '%s': release.\n", class->name); @@ -78,7 +78,7 @@ int class_create_file(struct class *cls, const struct class_attribute *attr) { int error; if (cls) - error = sysfs_create_file(&cls->subsys.kobj, &attr->attr); + error = sysfs_create_file(&cls->p->subsys.kobj, &attr->attr); else error = -EINVAL; return error; @@ -87,21 +87,20 @@ int class_create_file(struct class *cls, const struct class_attribute *attr) void class_remove_file(struct class *cls, const struct class_attribute *attr) { if (cls) - sysfs_remove_file(&cls->subsys.kobj, &attr->attr); + sysfs_remove_file(&cls->p->subsys.kobj, &attr->attr); } static struct class *class_get(struct class *cls) { if (cls) - return container_of(kset_get(&cls->subsys), - struct class, subsys); - return NULL; + kset_get(&cls->p->subsys); + return cls; } static void class_put(struct class *cls) { if (cls) - kset_put(&cls->subsys); + kset_put(&cls->p->subsys); } static int add_class_attrs(struct class *cls) @@ -136,17 +135,23 @@ static void remove_class_attrs(struct class *cls) int class_register(struct class *cls) { + struct class_private *cp; int error; pr_debug("device class '%s': registering\n", cls->name); - INIT_LIST_HEAD(&cls->devices); - INIT_LIST_HEAD(&cls->interfaces); - kset_init(&cls->class_dirs); - init_MUTEX(&cls->sem); - error = kobject_set_name(&cls->subsys.kobj, "%s", cls->name); - if (error) + cp = kzalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + INIT_LIST_HEAD(&cp->devices); + INIT_LIST_HEAD(&cp->interfaces); + kset_init(&cp->class_dirs); + init_MUTEX(&cp->sem); + error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name); + if (error) { + kfree(cp); return error; + } /* set the default /sys/dev directory for devices of this class */ if (!cls->dev_kobj) @@ -155,17 +160,21 @@ int class_register(struct class *cls) #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK) /* let the block class directory show up in the root of sysfs */ if (cls != &block_class) - cls->subsys.kobj.kset = class_kset; + cp->subsys.kobj.kset = class_kset; #else - cls->subsys.kobj.kset = class_kset; + cp->subsys.kobj.kset = class_kset; #endif - cls->subsys.kobj.ktype = &class_ktype; + cp->subsys.kobj.ktype = &class_ktype; + cp->class = cls; + cls->p = cp; - error = kset_register(&cls->subsys); - if (!error) { - error = add_class_attrs(class_get(cls)); - class_put(cls); + error = kset_register(&cp->subsys); + if (error) { + kfree(cp); + return error; } + error = add_class_attrs(class_get(cls)); + class_put(cls); return error; } @@ -173,7 +182,7 @@ void class_unregister(struct class *cls) { pr_debug("device class '%s': unregistering\n", cls->name); remove_class_attrs(cls); - kset_unregister(&cls->subsys); + kset_unregister(&cls->p->subsys); } static void class_create_release(struct class *cls) @@ -280,8 +289,8 @@ int class_for_each_device(struct class *class, struct device *start, if (!class) return -EINVAL; - down(&class->sem); - list_for_each_entry(dev, &class->devices, node) { + down(&class->p->sem); + list_for_each_entry(dev, &class->p->devices, node) { if (start) { if (start == dev) start = NULL; @@ -293,7 +302,7 @@ int class_for_each_device(struct class *class, struct device *start, if (error) break; } - up(&class->sem); + up(&class->p->sem); return error; } @@ -330,8 +339,8 @@ struct device *class_find_device(struct class *class, struct device *start, if (!class) return NULL; - down(&class->sem); - list_for_each_entry(dev, &class->devices, node) { + down(&class->p->sem); + list_for_each_entry(dev, &class->p->devices, node) { if (start) { if (start == dev) start = NULL; @@ -344,7 +353,7 @@ struct device *class_find_device(struct class *class, struct device *start, } else put_device(dev); } - up(&class->sem); + up(&class->p->sem); return found ? dev : NULL; } @@ -362,13 +371,13 @@ int class_interface_register(struct class_interface *class_intf) if (!parent) return -EINVAL; - down(&parent->sem); - list_add_tail(&class_intf->node, &parent->interfaces); + down(&parent->p->sem); + list_add_tail(&class_intf->node, &parent->p->interfaces); if (class_intf->add_dev) { - list_for_each_entry(dev, &parent->devices, node) + list_for_each_entry(dev, &parent->p->devices, node) class_intf->add_dev(dev, class_intf); } - up(&parent->sem); + up(&parent->p->sem); return 0; } @@ -381,13 +390,13 @@ void class_interface_unregister(struct class_interface *class_intf) if (!parent) return; - down(&parent->sem); + down(&parent->p->sem); list_del_init(&class_intf->node); if (class_intf->remove_dev) { - list_for_each_entry(dev, &parent->devices, node) + list_for_each_entry(dev, &parent->p->devices, node) class_intf->remove_dev(dev, class_intf); } - up(&parent->sem); + up(&parent->p->sem); class_put(parent); } diff --git a/drivers/base/core.c b/drivers/base/core.c index 9f05de6f80b5..64c150b5a883 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -551,7 +551,7 @@ static struct kobject *get_device_parent(struct device *dev, { /* class devices without a parent live in /sys/class// */ if (dev->class && (!parent || parent->class != dev->class)) - return &dev->class->subsys.kobj; + return &dev->class->p->subsys.kobj; /* all other devices keep their parent */ else if (parent) return &parent->kobj; @@ -597,13 +597,13 @@ static struct kobject *get_device_parent(struct device *dev, parent_kobj = &parent->kobj; /* find our class-directory at the parent and reference it */ - spin_lock(&dev->class->class_dirs.list_lock); - list_for_each_entry(k, &dev->class->class_dirs.list, entry) + spin_lock(&dev->class->p->class_dirs.list_lock); + list_for_each_entry(k, &dev->class->p->class_dirs.list, entry) if (k->parent == parent_kobj) { kobj = kobject_get(k); break; } - spin_unlock(&dev->class->class_dirs.list_lock); + spin_unlock(&dev->class->p->class_dirs.list_lock); if (kobj) return kobj; @@ -611,7 +611,7 @@ static struct kobject *get_device_parent(struct device *dev, k = kobject_create(); if (!k) return NULL; - k->kset = &dev->class->class_dirs; + k->kset = &dev->class->p->class_dirs; retval = kobject_add(k, parent_kobj, "%s", dev->class->name); if (retval < 0) { kobject_put(k); @@ -630,7 +630,7 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir) { /* see if we live in a "glue" directory */ if (!glue_dir || !dev->class || - glue_dir->kset != &dev->class->class_dirs) + glue_dir->kset != &dev->class->p->class_dirs) return; kobject_put(glue_dir); @@ -657,17 +657,17 @@ static int device_add_class_symlinks(struct device *dev) if (!dev->class) return 0; - error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, + error = sysfs_create_link(&dev->kobj, &dev->class->p->subsys.kobj, "subsystem"); if (error) goto out; #ifdef CONFIG_SYSFS_DEPRECATED /* stacked class devices need a symlink in the class directory */ - if (dev->kobj.parent != &dev->class->subsys.kobj && + if (dev->kobj.parent != &dev->class->p->subsys.kobj && device_is_not_partition(dev)) { - error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, - dev->bus_id); + error = sysfs_create_link(&dev->class->p->subsys.kobj, + &dev->kobj, dev->bus_id); if (error) goto out_subsys; } @@ -704,12 +704,12 @@ out_device: if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); out_busid: - if (dev->kobj.parent != &dev->class->subsys.kobj && + if (dev->kobj.parent != &dev->class->p->subsys.kobj && device_is_not_partition(dev)) - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->subsys.kobj, dev->bus_id); #else /* link in the class directory pointing to the device */ - error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, + error = sysfs_create_link(&dev->class->p->subsys.kobj, &dev->kobj, dev->bus_id); if (error) goto out_subsys; @@ -723,7 +723,7 @@ out_busid: return 0; out_busid: - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->subsys.kobj, dev->bus_id); #endif out_subsys: @@ -749,14 +749,14 @@ static void device_remove_class_symlinks(struct device *dev) sysfs_remove_link(&dev->kobj, "device"); } - if (dev->kobj.parent != &dev->class->subsys.kobj && + if (dev->kobj.parent != &dev->class->p->subsys.kobj && device_is_not_partition(dev)) - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->subsys.kobj, dev->bus_id); #else if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); - sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->class->p->subsys.kobj, dev->bus_id); #endif sysfs_remove_link(&dev->kobj, "subsystem"); @@ -904,15 +904,16 @@ int device_add(struct device *dev) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { - down(&dev->class->sem); + down(&dev->class->p->sem); /* tie the class to the device */ - list_add_tail(&dev->node, &dev->class->devices); + list_add_tail(&dev->node, &dev->class->p->devices); /* notify any interfaces that the device is here */ - list_for_each_entry(class_intf, &dev->class->interfaces, node) + list_for_each_entry(class_intf, &dev->class->p->interfaces, + node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); - up(&dev->class->sem); + up(&dev->class->p->sem); } Done: put_device(dev); @@ -1013,14 +1014,15 @@ void device_del(struct device *dev) if (dev->class) { device_remove_class_symlinks(dev); - down(&dev->class->sem); + down(&dev->class->p->sem); /* notify any interfaces that the device is now gone */ - list_for_each_entry(class_intf, &dev->class->interfaces, node) + list_for_each_entry(class_intf, &dev->class->p->interfaces, + node) if (class_intf->remove_dev) class_intf->remove_dev(dev, class_intf); /* remove the device from the class list */ list_del_init(&dev->node); - up(&dev->class->sem); + up(&dev->class->p->sem); } device_remove_file(dev, &uevent_attr); device_remove_attrs(dev); @@ -1348,11 +1350,12 @@ int device_rename(struct device *dev, char *new_name) } #else if (dev->class) { - error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, - dev->bus_id); + error = sysfs_create_link(&dev->class->p->subsys.kobj, + &dev->kobj, dev->bus_id); if (error) goto out; - sysfs_remove_link(&dev->class->subsys.kobj, old_device_name); + sysfs_remove_link(&dev->class->p->subsys.kobj, + old_device_name); } #endif diff --git a/include/linux/device.h b/include/linux/device.h index c1f72984875f..b0556082179b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -35,6 +35,7 @@ struct device; struct device_driver; struct driver_private; struct class; +struct class_private; struct bus_type; struct bus_type_private; @@ -186,11 +187,6 @@ struct class { const char *name; struct module *owner; - struct kset subsys; - struct list_head devices; - struct list_head interfaces; - struct kset class_dirs; - struct semaphore sem; /* locks children, devices, interfaces */ struct class_attribute *class_attrs; struct device_attribute *dev_attrs; struct kobject *dev_kobj; @@ -204,6 +200,7 @@ struct class { int (*resume)(struct device *dev); struct pm_ops *pm; + struct class_private *p; }; extern struct kobject *sysfs_dev_block_kobj; -- cgit From d2a3b9146e4f40c2e872d7567c996ef95083d802 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 28 May 2008 09:28:39 -0700 Subject: class: add lockdep infrastructure This adds the infrastructure to properly handle lockdep issues when the internal class semaphore is changed to a mutex. Matthew wrote the original patch, and Greg fixed it up to work properly with the class_create() function. From: Matthew Wilcox Cc: Kay Sievers Cc: Dave Young Cc: Andrew Morton Cc: James Bottomley Cc: Peter Zijlstra Cc: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 11 ++++++----- include/linux/device.h | 25 +++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index d24d21114ccb..89000566690c 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -134,7 +134,7 @@ static void remove_class_attrs(struct class *cls) } } -int class_register(struct class *cls) +int __class_register(struct class *cls, struct lock_class_key *key) { struct class_private *cp; int error; @@ -178,6 +178,7 @@ int class_register(struct class *cls) class_put(cls); return error; } +EXPORT_SYMBOL_GPL(__class_register); void class_unregister(struct class *cls) { @@ -203,7 +204,8 @@ static void class_create_release(struct class *cls) * Note, the pointer created here is to be destroyed when finished by * making a call to class_destroy(). */ -struct class *class_create(struct module *owner, const char *name) +struct class *__class_create(struct module *owner, const char *name, + struct lock_class_key *key) { struct class *cls; int retval; @@ -218,7 +220,7 @@ struct class *class_create(struct module *owner, const char *name) cls->owner = owner; cls->class_release = class_create_release; - retval = class_register(cls); + retval = __class_register(cls, key); if (retval) goto error; @@ -228,6 +230,7 @@ error: kfree(cls); return ERR_PTR(retval); } +EXPORT_SYMBOL_GPL(__class_create); /** * class_destroy - destroys a struct class structure @@ -412,9 +415,7 @@ int __init classes_init(void) EXPORT_SYMBOL_GPL(class_create_file); EXPORT_SYMBOL_GPL(class_remove_file); -EXPORT_SYMBOL_GPL(class_register); EXPORT_SYMBOL_GPL(class_unregister); -EXPORT_SYMBOL_GPL(class_create); EXPORT_SYMBOL_GPL(class_destroy); EXPORT_SYMBOL_GPL(class_interface_register); diff --git a/include/linux/device.h b/include/linux/device.h index b0556082179b..0e1d24c2ed41 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -205,8 +206,18 @@ struct class { extern struct kobject *sysfs_dev_block_kobj; extern struct kobject *sysfs_dev_char_kobj; -extern int __must_check class_register(struct class *class); +extern int __must_check __class_register(struct class *class, + struct lock_class_key *key); extern void class_unregister(struct class *class); + +/* This is a #define to keep the compiler from merging different + * instances of the __key variable */ +#define class_register(class) \ +({ \ + static struct lock_class_key __key; \ + __class_register(class, &__key); \ +}) + extern int class_for_each_device(struct class *class, struct device *start, void *data, int (*fn)(struct device *dev, void *data)); @@ -239,9 +250,19 @@ struct class_interface { extern int __must_check class_interface_register(struct class_interface *); extern void class_interface_unregister(struct class_interface *); -extern struct class *class_create(struct module *owner, const char *name); +extern struct class * __must_check __class_create(struct module *owner, + const char *name, + struct lock_class_key *key); extern void class_destroy(struct class *cls); +/* This is a #define to keep the compiler from merging different + * instances of the __key variable */ +#define class_create(owner, name) \ +({ \ + static struct lock_class_key __key; \ + __class_create(owner, name, &__key); \ +}) + /* * The type of device, "struct device" is embedded in. A class * or bus can contain devices of different types -- cgit From aab0de245150c09e61c30962feb16aacde508dc3 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 2 May 2008 06:02:41 +0200 Subject: driver core: remove KOBJ_NAME_LEN define Kobjects do not have a limit in name size since a while, so stop pretending that they do. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- arch/arm/plat-omap/mailbox.c | 2 +- arch/sparc64/kernel/vio.c | 2 +- drivers/message/fusion/mptbase.c | 3 ++- drivers/message/fusion/mptbase.h | 4 ++-- drivers/message/fusion/mptfc.c | 4 ++-- drivers/pci/hotplug/acpiphp.h | 4 ++-- drivers/scsi/hosts.c | 4 ++-- drivers/scsi/scsi_transport_fc.c | 9 +++++---- drivers/scsi/scsi_transport_iscsi.c | 4 ++-- fs/partitions/check.c | 2 +- include/linux/device.h | 3 +-- include/linux/kobject.h | 1 - include/linux/spi/spi.h | 2 +- include/scsi/scsi_host.h | 2 +- include/scsi/scsi_transport_fc.h | 4 ++-- include/scsi/scsi_transport_iscsi.h | 2 +- 16 files changed, 26 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c index 6f33f58bca45..848fdcafaa28 100644 --- a/arch/arm/plat-omap/mailbox.c +++ b/arch/arm/plat-omap/mailbox.c @@ -334,7 +334,7 @@ static int omap_mbox_init(struct omap_mbox *mbox) } mbox->dev.class = &omap_mbox_class; - strlcpy(mbox->dev.bus_id, mbox->name, KOBJ_NAME_LEN); + strlcpy(mbox->dev.bus_id, mbox->name, BUS_ID_SIZE); dev_set_drvdata(&mbox->dev, mbox); ret = device_register(&mbox->dev); diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c index e78b3517940b..ecbb8b618b8c 100644 --- a/arch/sparc64/kernel/vio.c +++ b/arch/sparc64/kernel/vio.c @@ -224,7 +224,7 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, if (!strcmp(type, "domain-services-port")) bus_id_name = "ds"; - if (strlen(bus_id_name) >= KOBJ_NAME_LEN - 4) { + if (strlen(bus_id_name) >= BUS_ID_SIZE - 4) { printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n", bus_id_name); return NULL; diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 75e599b85b64..34402c47027e 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -1670,7 +1670,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work); spin_lock_init(&ioc->fault_reset_work_lock); - snprintf(ioc->reset_work_q_name, KOBJ_NAME_LEN, "mpt_poll_%d", ioc->id); + snprintf(ioc->reset_work_q_name, sizeof(ioc->reset_work_q_name), + "mpt_poll_%d", ioc->id); ioc->reset_work_q = create_singlethread_workqueue(ioc->reset_work_q_name); if (!ioc->reset_work_q) { diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index 6adab648dbb9..dff048cfa101 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -707,12 +707,12 @@ typedef struct _MPT_ADAPTER u8 fc_link_speed[2]; spinlock_t fc_rescan_work_lock; struct work_struct fc_rescan_work; - char fc_rescan_work_q_name[KOBJ_NAME_LEN]; + char fc_rescan_work_q_name[20]; struct workqueue_struct *fc_rescan_work_q; struct scsi_cmnd **ScsiLookup; spinlock_t scsi_lookup_lock; - char reset_work_q_name[KOBJ_NAME_LEN]; + char reset_work_q_name[20]; struct workqueue_struct *reset_work_q; struct delayed_work fault_reset_work; spinlock_t fault_reset_work_lock; diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index fc31ca6829d8..b36cae9ec6db 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -1326,8 +1326,8 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* initialize workqueue */ - snprintf(ioc->fc_rescan_work_q_name, KOBJ_NAME_LEN, "mptfc_wq_%d", - sh->host_no); + snprintf(ioc->fc_rescan_work_q_name, sizeof(ioc->fc_rescan_work_q_name), + "mptfc_wq_%d", sh->host_no); ioc->fc_rescan_work_q = create_singlethread_workqueue(ioc->fc_rescan_work_q_name); if (!ioc->fc_rescan_work_q) diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index eecf7cbf4139..5a58b075dd8d 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -36,7 +36,7 @@ #define _ACPIPHP_H #include -#include /* for KOBJ_NAME_LEN */ +#include #include #include @@ -51,7 +51,7 @@ #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) /* name size which is used for entries in pcihpfs */ -#define SLOT_NAME_SIZE KOBJ_NAME_LEN /* {_SUN} */ +#define SLOT_NAME_SIZE 20 /* {_SUN} */ struct acpiphp_bridge; struct acpiphp_slot; diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 78dad28b70d5..fed0b02ebc1d 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -232,8 +232,8 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev) } if (shost->transportt->create_work_queue) { - snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d", - shost->host_no); + snprintf(shost->work_q_name, sizeof(shost->work_q_name), + "scsi_wq_%d", shost->host_no); shost->work_q = create_singlethread_workqueue( shost->work_q_name); if (!shost->work_q) { diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 5fd64e70029d..a272b9a2c869 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -417,15 +417,16 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, fc_host->next_vport_number = 0; fc_host->npiv_vports_inuse = 0; - snprintf(fc_host->work_q_name, KOBJ_NAME_LEN, "fc_wq_%d", - shost->host_no); + snprintf(fc_host->work_q_name, sizeof(fc_host->work_q_name), + "fc_wq_%d", shost->host_no); fc_host->work_q = create_singlethread_workqueue( fc_host->work_q_name); if (!fc_host->work_q) return -ENOMEM; - snprintf(fc_host->devloss_work_q_name, KOBJ_NAME_LEN, "fc_dl_%d", - shost->host_no); + snprintf(fc_host->devloss_work_q_name, + sizeof(fc_host->devloss_work_q_name), + "fc_dl_%d", shost->host_no); fc_host->devloss_work_q = create_singlethread_workqueue( fc_host->devloss_work_q_name); if (!fc_host->devloss_work_q) { diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 06748f318cd5..043c3921164f 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -247,8 +247,8 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev, atomic_set(&ihost->nr_scans, 0); mutex_init(&ihost->mutex); - snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d", - shost->host_no); + snprintf(ihost->scan_workq_name, sizeof(ihost->scan_workq_name), + "iscsi_scan_%d", shost->host_no); ihost->scan_workq = create_singlethread_workqueue( ihost->scan_workq_name); if (!ihost->scan_workq) diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 6149e4b58c88..efef715135d3 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -401,7 +401,7 @@ void register_disk(struct gendisk *disk) disk->dev.parent = disk->driverfs_dev; disk->dev.devt = MKDEV(disk->major, disk->first_minor); - strlcpy(disk->dev.bus_id, disk->disk_name, KOBJ_NAME_LEN); + strlcpy(disk->dev.bus_id, disk->disk_name, BUS_ID_SIZE); /* ewww... some of these buggers have / in the name... */ s = strchr(disk->dev.bus_id, '/'); if (s) diff --git a/include/linux/device.h b/include/linux/device.h index 0e1d24c2ed41..fba1bb0d1758 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -29,8 +29,7 @@ /* DEVICE_NAME_HALF is really less than half to accommodate slop */ #define DEVICE_NAME_HALF __stringify(20) #define DEVICE_ID_SIZE 32 -#define BUS_ID_SIZE KOBJ_NAME_LEN - +#define BUS_ID_SIZE 20 struct device; struct device_driver; diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 39e709f88aa0..d542faa6cb47 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -26,7 +26,6 @@ #include #include -#define KOBJ_NAME_LEN 20 #define UEVENT_HELPER_PATH_LEN 256 #define UEVENT_NUM_ENVP 32 /* number of env pointers */ #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 387e428f1cdf..b9a76c972084 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -733,7 +733,7 @@ struct spi_board_info { * controller_data goes to spi_device.controller_data, * irq is copied too */ - char modalias[KOBJ_NAME_LEN]; + char modalias[32]; const void *platform_data; void *controller_data; int irq; diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 1834fdfe82a7..a594bac4a77d 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -623,7 +623,7 @@ struct Scsi_Host { /* * Optional work queue to be utilized by the transport */ - char work_q_name[KOBJ_NAME_LEN]; + char work_q_name[20]; struct workqueue_struct *work_q; /* diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 06f72bab9df0..878373c32ef7 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -489,9 +489,9 @@ struct fc_host_attrs { u16 npiv_vports_inuse; /* work queues for rport state manipulation */ - char work_q_name[KOBJ_NAME_LEN]; + char work_q_name[20]; struct workqueue_struct *work_q; - char devloss_work_q_name[KOBJ_NAME_LEN]; + char devloss_work_q_name[20]; struct workqueue_struct *devloss_work_q; }; diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index f5444e033cc9..8b6c91df4c7a 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -198,7 +198,7 @@ struct iscsi_cls_host { atomic_t nr_scans; struct mutex mutex; struct workqueue_struct *scan_workq; - char scan_workq_name[KOBJ_NAME_LEN]; + char scan_workq_name[20]; }; extern void iscsi_host_for_each_session(struct Scsi_Host *shost, -- cgit From ca52a49846f1451163c0dc14c40cd06ff808df3e Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 2 May 2008 06:02:41 +0200 Subject: driver core: remove DEVICE_NAME_SIZE define There is no such thing as a "device name size" in the driver core, so remove the define and fix up any users of this odd define in the rest of the kernel. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/eisa/Makefile | 2 +- drivers/eisa/eisa-bus.c | 4 ++-- drivers/mca/mca-bus.c | 2 +- drivers/video/aty/aty128fb.c | 2 +- drivers/video/aty/radeonfb.h | 2 +- include/linux/device.h | 3 --- include/linux/eisa.h | 2 +- 7 files changed, 7 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/eisa/Makefile b/drivers/eisa/Makefile index 70abf93fe6b0..5369ce957c6d 100644 --- a/drivers/eisa/Makefile +++ b/drivers/eisa/Makefile @@ -9,7 +9,7 @@ obj-${CONFIG_EISA_VIRTUAL_ROOT} += virtual_root.o # Ugly hack to get DEVICE_NAME_SIZE value... -DEVICE_NAME_SIZE =$(shell awk '$$1=="\#define" && $$2=="DEVICE_NAME_SIZE" {print $$3-1}' $(srctree)/include/linux/device.h) +DEVICE_NAME_SIZE = 50 $(obj)/eisa-bus.o: $(obj)/devlist.h diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index 65dcf0432653..c950bf8606d9 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -22,7 +22,7 @@ struct eisa_device_info { struct eisa_device_id id; - char name[DEVICE_NAME_SIZE]; + char name[50]; }; #ifdef CONFIG_EISA_NAMES @@ -63,7 +63,7 @@ static void __init eisa_name_device (struct eisa_device *edev) if (!strcmp (edev->id.sig, eisa_table[i].id.sig)) { strlcpy (edev->pretty_name, eisa_table[i].name, - DEVICE_NAME_SIZE); + sizeof(edev->pretty_name)); return; } } diff --git a/drivers/mca/mca-bus.c b/drivers/mca/mca-bus.c index 67b8e9453b19..ef2dbfe74714 100644 --- a/drivers/mca/mca-bus.c +++ b/drivers/mca/mca-bus.c @@ -40,7 +40,7 @@ static struct mca_bus *mca_root_busses[MAX_MCA_BUSSES]; struct mca_device_info { short pos_id; /* the 2 byte pos id for this card */ - char name[DEVICE_NAME_SIZE]; + char name[50]; }; static int mca_bus_match (struct device *dev, struct device_driver *drv) diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 24ee96c4e9e9..07b6addbb3c1 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -1872,7 +1872,7 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i struct fb_info *info = pci_get_drvdata(pdev); struct aty128fb_par *par = info->par; struct fb_var_screeninfo var; - char video_card[DEVICE_NAME_SIZE]; + char video_card[50]; u8 chip_rev; u32 dac; diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h index c347e38cd0b0..ccbfffd12805 100644 --- a/drivers/video/aty/radeonfb.h +++ b/drivers/video/aty/radeonfb.h @@ -289,7 +289,7 @@ struct radeonfb_info { struct radeon_regs state; struct radeon_regs init_state; - char name[DEVICE_NAME_SIZE]; + char name[50]; unsigned long mmio_base_phys; unsigned long fb_base_phys; diff --git a/include/linux/device.h b/include/linux/device.h index fba1bb0d1758..894fdb2e483a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -25,9 +25,6 @@ #include #include -#define DEVICE_NAME_SIZE 50 -/* DEVICE_NAME_HALF is really less than half to accommodate slop */ -#define DEVICE_NAME_HALF __stringify(20) #define DEVICE_ID_SIZE 32 #define BUS_ID_SIZE 20 diff --git a/include/linux/eisa.h b/include/linux/eisa.h index fe806b6f030d..e61c0be2a459 100644 --- a/include/linux/eisa.h +++ b/include/linux/eisa.h @@ -40,7 +40,7 @@ struct eisa_device { u64 dma_mask; struct device dev; /* generic device */ #ifdef CONFIG_EISA_NAMES - char pretty_name[DEVICE_NAME_SIZE]; + char pretty_name[50]; #endif }; -- cgit From b98cb4b7fe0e83238501b48489e46b3e0dce9aaf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 2 May 2008 06:02:41 +0200 Subject: driver core: remove DEVICE_ID_SIZE define There is no such thing as a "device id size" in the driver core, so remove the define and fix up any users of this odd define in the rest of the kernel. Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/memstick/host/jmb38x_ms.c | 4 ++-- include/linux/device.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c index a054668eda16..4e3bfbcdf155 100644 --- a/drivers/memstick/host/jmb38x_ms.c +++ b/drivers/memstick/host/jmb38x_ms.c @@ -51,7 +51,7 @@ struct jmb38x_ms_host { void __iomem *addr; spinlock_t lock; int id; - char host_id[DEVICE_ID_SIZE]; + char host_id[32]; int irq; unsigned int block_pos; unsigned long timeout_jiffies; @@ -781,7 +781,7 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt) spin_lock_init(&host->lock); host->id = cnt; - snprintf(host->host_id, DEVICE_ID_SIZE, DRIVER_NAME ":slot%d", + snprintf(host->host_id, sizeof(host->host_id), DRIVER_NAME ":slot%d", host->id); host->irq = jm->pdev->irq; host->timeout_jiffies = msecs_to_jiffies(1000); diff --git a/include/linux/device.h b/include/linux/device.h index 894fdb2e483a..d24a47f80f9c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -25,7 +25,6 @@ #include #include -#define DEVICE_ID_SIZE 32 #define BUS_ID_SIZE 20 struct device; -- cgit From 328a14e70e7f46997cb50d4258dd93d5377f98c6 Mon Sep 17 00:00:00 2001 From: "Hans J. Koch" Date: Fri, 23 May 2008 13:50:14 +0200 Subject: UIO: Add write function to allow irq masking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes it is necessary to enable/disable the interrupt of a UIO device from the userspace part of the driver. With this patch, the UIO kernel driver can implement an "irqcontrol()" function that does this. Userspace can write an s32 value to /dev/uioX (usually 0 or 1 to turn the irq off or on). The UIO core will then call the driver's irqcontrol function. Signed-off-by: Hans J. Koch Acked-by: Uwe Kleine-König Acked-by: Magnus Damm Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/uio-howto.tmpl | 40 +++++++++++++++++++++++++++++++++++- drivers/uio/uio.c | 26 +++++++++++++++++++++++ include/linux/uio_driver.h | 2 ++ 3 files changed, 67 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl index fdd7f4f887b7..c4d187313963 100644 --- a/Documentation/DocBook/uio-howto.tmpl +++ b/Documentation/DocBook/uio-howto.tmpl @@ -29,6 +29,12 @@ + + 0.5 + 2008-05-22 + hjk + Added description of write() function. + 0.4 2007-11-26 @@ -64,7 +70,7 @@ Copyright and License - Copyright (c) 2006 by Hans-Jürgen Koch. + Copyright (c) 2006-2008 by Hans-Jürgen Koch. This documentation is Free Software licensed under the terms of the GPL version 2. @@ -189,6 +195,30 @@ interested in translating it, please email me represents the total interrupt count. You can use this number to figure out if you missed some interrupts. + + For some hardware that has more than one interrupt source internally, + but not separate IRQ mask and status registers, there might be + situations where userspace cannot determine what the interrupt source + was if the kernel handler disables them by writing to the chip's IRQ + register. In such a case, the kernel has to disable the IRQ completely + to leave the chip's register untouched. Now the userspace part can + determine the cause of the interrupt, but it cannot re-enable + interrupts. Another cornercase is chips where re-enabling interrupts + is a read-modify-write operation to a combined IRQ status/acknowledge + register. This would be racy if a new interrupt occurred + simultaneously. + + + To address these problems, UIO also implements a write() function. It + is normally not used and can be ignored for hardware that has only a + single interrupt source or has separate IRQ mask and status registers. + If you need it, however, a write to /dev/uioX + will call the irqcontrol() function implemented + by the driver. You have to write a 32-bit value that is usually either + 0 or 1 to disable or enable interrupts. If a driver does not implement + irqcontrol(), write() will + return with -ENOSYS. + To handle interrupts properly, your custom kernel module can @@ -362,6 +392,14 @@ device is actually used. open(), you will probably also want a custom release() function. + + +int (*irqcontrol)(struct uio_info *info, s32 irq_on) +: Optional. If you need to be able to enable or disable +interrupts from userspace by writing to /dev/uioX, +you can implement this function. The parameter irq_on +will be 0 to disable interrupts and 1 to enable them. + diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 5a7ca2e6094d..3a6934bf7131 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -427,6 +427,31 @@ static ssize_t uio_read(struct file *filep, char __user *buf, return retval; } +static ssize_t uio_write(struct file *filep, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct uio_listener *listener = filep->private_data; + struct uio_device *idev = listener->dev; + ssize_t retval; + s32 irq_on; + + if (idev->info->irq == UIO_IRQ_NONE) + return -EIO; + + if (count != sizeof(s32)) + return -EINVAL; + + if (!idev->info->irqcontrol) + return -ENOSYS; + + if (copy_from_user(&irq_on, buf, count)) + return -EFAULT; + + retval = idev->info->irqcontrol(idev->info, irq_on); + + return retval ? retval : sizeof(s32); +} + static int uio_find_mem_index(struct vm_area_struct *vma) { int mi; @@ -546,6 +571,7 @@ static const struct file_operations uio_fops = { .open = uio_open, .release = uio_release, .read = uio_read, + .write = uio_write, .mmap = uio_mmap, .poll = uio_poll, .fasync = uio_fasync, diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index 973386d439da..cf65e964102b 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h @@ -53,6 +53,7 @@ struct uio_device; * @mmap: mmap operation for this uio device * @open: open operation for this uio device * @release: release operation for this uio device + * @irqcontrol: disable/enable irqs when 0/1 is written to /dev/uioX */ struct uio_info { struct uio_device *uio_dev; @@ -66,6 +67,7 @@ struct uio_info { int (*mmap)(struct uio_info *info, struct vm_area_struct *vma); int (*open)(struct uio_info *info, struct inode *inode); int (*release)(struct uio_info *info, struct inode *inode); + int (*irqcontrol)(struct uio_info *info, s32 irq_on); }; extern int __must_check -- cgit From 6d8333c24d41637f0f847f6e17032189dae08c02 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 10 Jun 2008 09:14:48 +0200 Subject: UIO: minor style and comment fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Hans J. Koch --- include/linux/uio_driver.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index cf65e964102b..cdf338d94b7f 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h @@ -36,7 +36,7 @@ struct uio_mem { struct uio_map *map; }; -#define MAX_UIO_MAPS 5 +#define MAX_UIO_MAPS 5 struct uio_device; @@ -82,11 +82,11 @@ static inline int __must_check extern void uio_unregister_device(struct uio_info *info); extern void uio_event_notify(struct uio_info *info); -/* defines for uio_device->irq */ +/* defines for uio_info->irq */ #define UIO_IRQ_CUSTOM -1 #define UIO_IRQ_NONE -2 -/* defines for uio_device->memtype */ +/* defines for uio_mem->memtype */ #define UIO_MEM_NONE 0 #define UIO_MEM_PHYS 1 #define UIO_MEM_LOGICAL 2 -- cgit From a231934bdf086a4fefc0df06e669499125a9db6f Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Mon, 2 Jun 2008 11:07:25 +0100 Subject: kobject: reorder kobject to save space on 64 bit builds reorder kobject to save space on 64 bit builds. shrinks from 72 to 64 bytes & moves allocated kobject to a smaller slab. Signed-off-by: Richard Kennedy Signed-off-by: Greg Kroah-Hartman --- include/linux/kobject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index d542faa6cb47..60f0d418ae32 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -58,12 +58,12 @@ enum kobject_action { struct kobject { const char *name; - struct kref kref; struct list_head entry; struct kobject *parent; struct kset *kset; struct kobj_type *ktype; struct sysfs_dirent *sd; + struct kref kref; unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1; -- cgit From 9505e6375640fc61d92d36c8e9f25a6a218f3f57 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Tue, 1 Jul 2008 15:14:51 +0200 Subject: debugfs: Implement debugfs_remove_recursive() debugfs_remove_recursive() will remove a dentry and all its children. Drivers can use this to zap their whole debugfs tree so that they don't need to keep track of every single debugfs dentry they created. It may fail to remove the whole tree in certain cases: sh-3.2# rmmod atmel-mci < /sys/kernel/debug/mmc0/ios/clock mmc0: card b368 removed atmel_mci atmel_mci.0: Lost dma0chan1, falling back to PIO sh-3.2# ls /sys/kernel/debug/mmc0/ ios But I'm not sure if that case can be handled in any sane manner. Signed-off-by: Haavard Skinnemoen Cc: Pierre Ossman Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/inode.c | 114 ++++++++++++++++++++++++++++++++++++++++-------- include/linux/debugfs.h | 4 ++ 2 files changed, 100 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index e9602d85c11d..08e28c9bb416 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -309,6 +309,31 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, } EXPORT_SYMBOL_GPL(debugfs_create_symlink); +static void __debugfs_remove(struct dentry *dentry, struct dentry *parent) +{ + int ret = 0; + + if (debugfs_positive(dentry)) { + if (dentry->d_inode) { + dget(dentry); + switch (dentry->d_inode->i_mode & S_IFMT) { + case S_IFDIR: + ret = simple_rmdir(parent->d_inode, dentry); + break; + case S_IFLNK: + kfree(dentry->d_inode->i_private); + /* fall through */ + default: + simple_unlink(parent->d_inode, dentry); + break; + } + if (!ret) + d_delete(dentry); + dput(dentry); + } + } +} + /** * debugfs_remove - removes a file or directory from the debugfs filesystem * @dentry: a pointer to a the dentry of the file or directory to be @@ -325,7 +350,6 @@ EXPORT_SYMBOL_GPL(debugfs_create_symlink); void debugfs_remove(struct dentry *dentry) { struct dentry *parent; - int ret = 0; if (!dentry) return; @@ -335,29 +359,83 @@ void debugfs_remove(struct dentry *dentry) return; mutex_lock(&parent->d_inode->i_mutex); - if (debugfs_positive(dentry)) { - if (dentry->d_inode) { - dget(dentry); - switch (dentry->d_inode->i_mode & S_IFMT) { - case S_IFDIR: - ret = simple_rmdir(parent->d_inode, dentry); - break; - case S_IFLNK: - kfree(dentry->d_inode->i_private); - /* fall through */ - default: - simple_unlink(parent->d_inode, dentry); + __debugfs_remove(dentry, parent); + mutex_unlock(&parent->d_inode->i_mutex); + simple_release_fs(&debugfs_mount, &debugfs_mount_count); +} +EXPORT_SYMBOL_GPL(debugfs_remove); + +/** + * debugfs_remove_recursive - recursively removes a directory + * @dentry: a pointer to a the dentry of the directory to be removed. + * + * This function recursively removes a directory tree in debugfs that + * was previously created with a call to another debugfs function + * (like debugfs_create_file() or variants thereof.) + * + * This function is required to be called in order for the file to be + * removed, no automatic cleanup of files will happen when a module is + * removed, you are responsible here. + */ +void debugfs_remove_recursive(struct dentry *dentry) +{ + struct dentry *child; + struct dentry *parent; + + if (!dentry) + return; + + parent = dentry->d_parent; + if (!parent || !parent->d_inode) + return; + + parent = dentry; + mutex_lock(&parent->d_inode->i_mutex); + + while (1) { + /* + * When all dentries under "parent" has been removed, + * walk up the tree until we reach our starting point. + */ + if (list_empty(&parent->d_subdirs)) { + mutex_unlock(&parent->d_inode->i_mutex); + if (parent == dentry) break; - } - if (!ret) - d_delete(dentry); - dput(dentry); + parent = parent->d_parent; + mutex_lock(&parent->d_inode->i_mutex); + } + child = list_entry(parent->d_subdirs.next, struct dentry, + d_u.d_child); + + /* + * If "child" isn't empty, walk down the tree and + * remove all its descendants first. + */ + if (!list_empty(&child->d_subdirs)) { + mutex_unlock(&parent->d_inode->i_mutex); + parent = child; + mutex_lock(&parent->d_inode->i_mutex); + continue; } + __debugfs_remove(child, parent); + if (parent->d_subdirs.next == &child->d_u.d_child) { + /* + * Avoid infinite loop if we fail to remove + * one dentry. + */ + mutex_unlock(&parent->d_inode->i_mutex); + break; + } + simple_release_fs(&debugfs_mount, &debugfs_mount_count); } + + parent = dentry->d_parent; + mutex_lock(&parent->d_inode->i_mutex); + __debugfs_remove(dentry, parent); mutex_unlock(&parent->d_inode->i_mutex); simple_release_fs(&debugfs_mount, &debugfs_mount_count); } -EXPORT_SYMBOL_GPL(debugfs_remove); +EXPORT_SYMBOL_GPL(debugfs_remove_recursive); /** * debugfs_rename - rename a file/directory in the debugfs filesystem diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 32755cdf68db..e1a6c046cea3 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -44,6 +44,7 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, const char *dest); void debugfs_remove(struct dentry *dentry); +void debugfs_remove_recursive(struct dentry *dentry); struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, struct dentry *new_dir, const char *new_name); @@ -101,6 +102,9 @@ static inline struct dentry *debugfs_create_symlink(const char *name, static inline void debugfs_remove(struct dentry *dentry) { } +static inline void debugfs_remove_recursive(struct dentry *dentry) +{ } + static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, struct dentry *new_dir, char *new_name) { -- cgit From 36ce6dad6e3cb3f050ed41e0beac0070d2062b25 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 10 Jun 2008 11:09:08 +0200 Subject: driver core: Suppress sysfs warnings for device_rename(). driver core: Suppress sysfs warnings for device_rename(). Renaming network devices to an already existing name is not something we want sysfs to print a scary warning for, since the callers can deal with this correctly. So let's introduce sysfs_create_link_nowarn() which gets rid of the common warning. Signed-off-by: Cornelia Huck Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 9 +++++---- fs/sysfs/dir.c | 37 +++++++++++++++++++++++++++++++++++-- fs/sysfs/symlink.c | 41 +++++++++++++++++++++++++++++++++-------- fs/sysfs/sysfs.h | 1 + include/linux/sysfs.h | 10 ++++++++++ 5 files changed, 84 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index c05b1159023e..7d5c63c81a59 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1345,8 +1345,9 @@ int device_rename(struct device *dev, char *new_name) if (old_class_name) { new_class_name = make_class_name(dev->class->name, &dev->kobj); if (new_class_name) { - error = sysfs_create_link(&dev->parent->kobj, - &dev->kobj, new_class_name); + error = sysfs_create_link_nowarn(&dev->parent->kobj, + &dev->kobj, + new_class_name); if (error) goto out; sysfs_remove_link(&dev->parent->kobj, old_class_name); @@ -1354,8 +1355,8 @@ int device_rename(struct device *dev, char *new_name) } #else if (dev->class) { - error = sysfs_create_link(&dev->class->p->class_subsys.kobj, - &dev->kobj, dev->bus_id); + error = sysfs_create_link_nowarn(&dev->class->p->class_subsys.kobj, + &dev->kobj, dev->bus_id); if (error) goto out; sysfs_remove_link(&dev->class->p->class_subsys.kobj, diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 8c0e4b92574f..c1a7efb310bf 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -398,7 +398,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, } /** - * sysfs_add_one - add sysfs_dirent to parent + * __sysfs_add_one - add sysfs_dirent to parent without warning * @acxt: addrm context to use * @sd: sysfs_dirent to be added * @@ -417,7 +417,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, * 0 on success, -EEXIST if entry with the given name already * exists. */ -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) +int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) { if (sysfs_find_dirent(acxt->parent_sd, sd->s_name)) return -EEXIST; @@ -434,6 +434,39 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) return 0; } +/** + * sysfs_add_one - add sysfs_dirent to parent + * @acxt: addrm context to use + * @sd: sysfs_dirent to be added + * + * Get @acxt->parent_sd and set sd->s_parent to it and increment + * nlink of parent inode if @sd is a directory and link into the + * children list of the parent. + * + * This function should be called between calls to + * sysfs_addrm_start() and sysfs_addrm_finish() and should be + * passed the same @acxt as passed to sysfs_addrm_start(). + * + * LOCKING: + * Determined by sysfs_addrm_start(). + * + * RETURNS: + * 0 on success, -EEXIST if entry with the given name already + * exists. + */ +int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) +{ + int ret; + + ret = __sysfs_add_one(acxt, sd); + if (ret == -EEXIST) { + printk(KERN_WARNING "sysfs: duplicate filename '%s' " + "can not be created\n", sd->s_name); + WARN_ON(1); + } + return ret; +} + /** * sysfs_remove_one - remove sysfs_dirent from parent * @acxt: addrm context to use diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 817f5966edca..a3ba217fbe74 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -19,13 +19,8 @@ #include "sysfs.h" -/** - * sysfs_create_link - create symlink between two objects. - * @kobj: object whose directory we're creating the link in. - * @target: object we're pointing to. - * @name: name of the symlink. - */ -int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) +static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, + const char *name, int warn) { struct sysfs_dirent *parent_sd = NULL; struct sysfs_dirent *target_sd = NULL; @@ -65,7 +60,10 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char target_sd = NULL; /* reference is now owned by the symlink */ sysfs_addrm_start(&acxt, parent_sd); - error = sysfs_add_one(&acxt, sd); + if (warn) + error = sysfs_add_one(&acxt, sd); + else + error = __sysfs_add_one(&acxt, sd); sysfs_addrm_finish(&acxt); if (error) @@ -79,6 +77,33 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char return error; } +/** + * sysfs_create_link - create symlink between two objects. + * @kobj: object whose directory we're creating the link in. + * @target: object we're pointing to. + * @name: name of the symlink. + */ +int sysfs_create_link(struct kobject *kobj, struct kobject *target, + const char *name) +{ + return sysfs_do_create_link(kobj, target, name, 1); +} + +/** + * sysfs_create_link_nowarn - create symlink between two objects. + * @kobj: object whose directory we're creating the link in. + * @target: object we're pointing to. + * @name: name of the symlink. + * + * This function does the same as sysf_create_link(), but it + * doesn't warn if the link already exists. + */ +int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target, + const char *name) +{ + return sysfs_do_create_link(kobj, target, name, 0); +} + /** * sysfs_remove_link - remove symlink in object's directory. * @kobj: object we're acting for. diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index ce4e15f8aaeb..a5db496f71c7 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -107,6 +107,7 @@ struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd); void sysfs_put_active_two(struct sysfs_dirent *sd); void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *parent_sd); +int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd); int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd); void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd); void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 7858eac40aa7..37fa24152bd8 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -101,6 +101,9 @@ void sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr); int __must_check sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name); +int __must_check sysfs_create_link_nowarn(struct kobject *kobj, + struct kobject *target, + const char *name); void sysfs_remove_link(struct kobject *kobj, const char *name); int __must_check sysfs_create_group(struct kobject *kobj, @@ -180,6 +183,13 @@ static inline int sysfs_create_link(struct kobject *kobj, return 0; } +static inline int sysfs_create_link_nowarn(struct kobject *kobj, + struct kobject *target, + const char *name) +{ + return 0; +} + static inline void sysfs_remove_link(struct kobject *kobj, const char *name) { } -- cgit From 4a0b2b4dbe1335b8b9886ba3dc85a145d5d938ed Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Jul 2008 18:48:41 +0200 Subject: sysdev: Pass the attribute to the low level sysdev show/store function This allow to dynamically generate attributes and share show/store functions between attributes. Right now most attributes are generated by special macros and lots of duplicated code. With the attribute passed it's instead possible to attach some data to the attribute and then use that in shared low level functions to do different things. I need this for the dynamically generated bank attributes in the x86 machine check code, but it'll allow some further cleanups. I converted all users in tree to the new show/store prototype. It's a single huge patch to avoid unbisectable sections. Runtime tested: x86-32, x86-64 Compiled only: ia64, powerpc Not compile tested/only grep converted: sh, arm, avr32 Signed-off-by: Andi Kleen Signed-off-by: Greg Kroah-Hartman --- arch/arm/kernel/time.c | 4 ++- arch/avr32/kernel/cpu.c | 38 +++++++++++++++++--------- arch/ia64/kernel/err_inject.c | 22 ++++++++++----- arch/powerpc/kernel/sysfs.c | 15 ++++++++--- arch/powerpc/platforms/cell/cbe_thermal.c | 45 ++++++++++++++++++++----------- arch/powerpc/platforms/cell/spu_base.c | 3 ++- arch/s390/kernel/smp.c | 36 ++++++++++++++++--------- arch/s390/kernel/time.c | 35 ++++++++++++++++-------- arch/sh/drivers/dma/dma-sysfs.c | 15 ++++++++--- arch/sparc64/kernel/sysfs.c | 16 +++++++---- arch/x86/kernel/cpu/mcheck/mce_64.c | 14 +++++++--- arch/x86/kernel/cpu/mcheck/therm_throt.c | 1 + arch/x86/kernel/microcode.c | 10 ++++--- drivers/base/cpu.c | 10 ++++--- drivers/base/memory.c | 12 ++++++--- drivers/base/node.c | 15 +++++++---- drivers/base/sys.c | 4 +-- drivers/base/topology.c | 17 ++++++++---- drivers/cpuidle/sysfs.c | 10 ++++--- drivers/xen/balloon.c | 1 + include/linux/sysdev.h | 5 ++-- kernel/rtmutex-tester.c | 7 ++--- kernel/sched.c | 8 ++++-- kernel/time/clocksource.c | 8 ++++-- 24 files changed, 239 insertions(+), 112 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index cc5145b28e7f..368d171754cf 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -130,7 +130,9 @@ static const struct leds_evt_name evt_names[] = { { "red", led_red_on, led_red_off }, }; -static ssize_t leds_store(struct sys_device *dev, const char *buf, size_t size) +static ssize_t leds_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t size) { int ret = -EINVAL, len = strcspn(buf, " "); diff --git a/arch/avr32/kernel/cpu.c b/arch/avr32/kernel/cpu.c index b8409caeb23d..e84faffbbeca 100644 --- a/arch/avr32/kernel/cpu.c +++ b/arch/avr32/kernel/cpu.c @@ -26,14 +26,16 @@ static DEFINE_PER_CPU(struct cpu, cpu_devices); * XXX: If/when a SMP-capable implementation of AVR32 will ever be * made, we must make sure that the code executes on the correct CPU. */ -static ssize_t show_pc0event(struct sys_device *dev, char *buf) +static ssize_t show_pc0event(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pccr; pccr = sysreg_read(PCCR); return sprintf(buf, "0x%lx\n", (pccr >> 12) & 0x3f); } -static ssize_t store_pc0event(struct sys_device *dev, const char *buf, +static ssize_t store_pc0event(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long val; @@ -46,15 +48,17 @@ static ssize_t store_pc0event(struct sys_device *dev, const char *buf, sysreg_write(PCCR, val); return count; } -static ssize_t show_pc0count(struct sys_device *dev, char *buf) +static ssize_t show_pc0count(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pcnt0; pcnt0 = sysreg_read(PCNT0); return sprintf(buf, "%lu\n", pcnt0); } -static ssize_t store_pc0count(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t store_pc0count(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { unsigned long val; char *endp; @@ -67,14 +71,16 @@ static ssize_t store_pc0count(struct sys_device *dev, const char *buf, return count; } -static ssize_t show_pc1event(struct sys_device *dev, char *buf) +static ssize_t show_pc1event(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pccr; pccr = sysreg_read(PCCR); return sprintf(buf, "0x%lx\n", (pccr >> 18) & 0x3f); } -static ssize_t store_pc1event(struct sys_device *dev, const char *buf, +static ssize_t store_pc1event(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long val; @@ -87,14 +93,16 @@ static ssize_t store_pc1event(struct sys_device *dev, const char *buf, sysreg_write(PCCR, val); return count; } -static ssize_t show_pc1count(struct sys_device *dev, char *buf) +static ssize_t show_pc1count(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pcnt1; pcnt1 = sysreg_read(PCNT1); return sprintf(buf, "%lu\n", pcnt1); } -static ssize_t store_pc1count(struct sys_device *dev, const char *buf, +static ssize_t store_pc1count(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long val; @@ -108,14 +116,16 @@ static ssize_t store_pc1count(struct sys_device *dev, const char *buf, return count; } -static ssize_t show_pccycles(struct sys_device *dev, char *buf) +static ssize_t show_pccycles(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pccnt; pccnt = sysreg_read(PCCNT); return sprintf(buf, "%lu\n", pccnt); } -static ssize_t store_pccycles(struct sys_device *dev, const char *buf, +static ssize_t store_pccycles(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long val; @@ -129,14 +139,16 @@ static ssize_t store_pccycles(struct sys_device *dev, const char *buf, return count; } -static ssize_t show_pcenable(struct sys_device *dev, char *buf) +static ssize_t show_pcenable(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned long pccr; pccr = sysreg_read(PCCR); return sprintf(buf, "%c\n", (pccr & 1)?'1':'0'); } -static ssize_t store_pcenable(struct sys_device *dev, const char *buf, +static ssize_t store_pcenable(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { unsigned long pccr, val; diff --git a/arch/ia64/kernel/err_inject.c b/arch/ia64/kernel/err_inject.c index b642648cc2ac..c539c689493b 100644 --- a/arch/ia64/kernel/err_inject.c +++ b/arch/ia64/kernel/err_inject.c @@ -55,7 +55,8 @@ static u64 resources[NR_CPUS]; #define show(name) \ static ssize_t \ -show_##name(struct sys_device *dev, char *buf) \ +show_##name(struct sys_device *dev, struct sysdev_attribute *attr, \ + char *buf) \ { \ u32 cpu=dev->id; \ return sprintf(buf, "%lx\n", name[cpu]); \ @@ -63,7 +64,8 @@ show_##name(struct sys_device *dev, char *buf) \ #define store(name) \ static ssize_t \ -store_##name(struct sys_device *dev, const char *buf, size_t size) \ +store_##name(struct sys_device *dev, struct sysdev_attribute *attr, \ + const char *buf, size_t size) \ { \ unsigned int cpu=dev->id; \ name[cpu] = simple_strtoull(buf, NULL, 16); \ @@ -76,7 +78,8 @@ show(call_start) * processor. The cpu number in driver is only used for storing data. */ static ssize_t -store_call_start(struct sys_device *dev, const char *buf, size_t size) +store_call_start(struct sys_device *dev, struct sysdev_attribute *attr, + const char *buf, size_t size) { unsigned int cpu=dev->id; unsigned long call_start = simple_strtoull(buf, NULL, 16); @@ -124,14 +127,16 @@ show(err_type_info) store(err_type_info) static ssize_t -show_virtual_to_phys(struct sys_device *dev, char *buf) +show_virtual_to_phys(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { unsigned int cpu=dev->id; return sprintf(buf, "%lx\n", phys_addr[cpu]); } static ssize_t -store_virtual_to_phys(struct sys_device *dev, const char *buf, size_t size) +store_virtual_to_phys(struct sys_device *dev, struct sysdev_attribute *attr, + const char *buf, size_t size) { unsigned int cpu=dev->id; u64 virt_addr=simple_strtoull(buf, NULL, 16); @@ -154,7 +159,8 @@ show(err_struct_info) store(err_struct_info) static ssize_t -show_err_data_buffer(struct sys_device *dev, char *buf) +show_err_data_buffer(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned int cpu=dev->id; @@ -165,7 +171,9 @@ show_err_data_buffer(struct sys_device *dev, char *buf) } static ssize_t -store_err_data_buffer(struct sys_device *dev, const char *buf, size_t size) +store_err_data_buffer(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t size) { unsigned int cpu=dev->id; int ret; diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index c8127f832df0..aba0ba95f062 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -28,7 +28,9 @@ static DEFINE_PER_CPU(struct cpu, cpu_devices); /* Time in microseconds we delay before sleeping in the idle loop */ DEFINE_PER_CPU(unsigned long, smt_snooze_delay) = { 100 }; -static ssize_t store_smt_snooze_delay(struct sys_device *dev, const char *buf, +static ssize_t store_smt_snooze_delay(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); @@ -44,7 +46,9 @@ static ssize_t store_smt_snooze_delay(struct sys_device *dev, const char *buf, return count; } -static ssize_t show_smt_snooze_delay(struct sys_device *dev, char *buf) +static ssize_t show_smt_snooze_delay(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); @@ -152,14 +156,17 @@ static unsigned long write_##NAME(unsigned long val) \ mtspr(ADDRESS, val); \ return 0; \ } \ -static ssize_t show_##NAME(struct sys_device *dev, char *buf) \ +static ssize_t show_##NAME(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ { \ struct cpu *cpu = container_of(dev, struct cpu, sysdev); \ unsigned long val = run_on_cpu(cpu->sysdev.id, read_##NAME, 0); \ return sprintf(buf, "%lx\n", val); \ } \ static ssize_t __used \ - store_##NAME(struct sys_device *dev, const char *buf, size_t count) \ + store_##NAME(struct sys_device *dev, struct sysdev_attribute *attr, \ + const char *buf, size_t count) \ { \ struct cpu *cpu = container_of(dev, struct cpu, sysdev); \ unsigned long val; \ diff --git a/arch/powerpc/platforms/cell/cbe_thermal.c b/arch/powerpc/platforms/cell/cbe_thermal.c index 4852bf312d83..4d4c8c169124 100644 --- a/arch/powerpc/platforms/cell/cbe_thermal.c +++ b/arch/powerpc/platforms/cell/cbe_thermal.c @@ -97,7 +97,8 @@ static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iom return value.spe[spu->spe_id]; } -static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_temp(struct sys_device *sysdev, struct sysdev_attribute *attr, + char *buf) { u8 value; struct cbe_pmd_regs __iomem *pmd_regs; @@ -146,32 +147,38 @@ static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char return size; } -static ssize_t spu_show_throttle_end(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(get_pmd_regs(sysdev), buf, 0); } -static ssize_t spu_show_throttle_begin(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(get_pmd_regs(sysdev), buf, 8); } -static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev, char *buf) +static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(get_pmd_regs(sysdev), buf, 16); } -static ssize_t spu_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t spu_store_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(get_pmd_regs(sysdev), buf, size, 0); } -static ssize_t spu_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t spu_store_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(get_pmd_regs(sysdev), buf, size, 8); } -static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(get_pmd_regs(sysdev), buf, size, 16); } @@ -192,43 +199,51 @@ static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos) /* shows the temperature of the DTS on the PPE, * located near the linear thermal sensor */ -static ssize_t ppe_show_temp0(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_temp0(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return ppe_show_temp(sysdev, buf, 32); } /* shows the temperature of the second DTS on the PPE */ -static ssize_t ppe_show_temp1(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_temp1(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return ppe_show_temp(sysdev, buf, 0); } -static ssize_t ppe_show_throttle_end(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 32); } -static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 40); } -static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev, char *buf) +static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 48); } -static ssize_t ppe_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t ppe_store_throttle_end(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 32); } -static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 40); } -static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size) +static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev, + struct sysdev_attribute *attr, const char *buf, size_t size) { return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 48); } diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 78f905bc6a42..a5bdb89a17c3 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -703,7 +703,8 @@ static unsigned long long spu_acct_time(struct spu *spu, } -static ssize_t spu_stat_show(struct sys_device *sysdev, char *buf) +static ssize_t spu_stat_show(struct sys_device *sysdev, + struct sysdev_attribute *attr, char *buf) { struct spu *spu = container_of(sysdev, struct spu, sysdev); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index b6781030cfbd..b795b3e24afd 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -864,7 +864,8 @@ int setup_profiling_timer(unsigned int multiplier) } #ifdef CONFIG_HOTPLUG_CPU -static ssize_t cpu_configure_show(struct sys_device *dev, char *buf) +static ssize_t cpu_configure_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t count; @@ -874,8 +875,9 @@ static ssize_t cpu_configure_show(struct sys_device *dev, char *buf) return count; } -static ssize_t cpu_configure_store(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t cpu_configure_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { int cpu = dev->id; int val, rc; @@ -922,7 +924,8 @@ out: static SYSDEV_ATTR(configure, 0644, cpu_configure_show, cpu_configure_store); #endif /* CONFIG_HOTPLUG_CPU */ -static ssize_t cpu_polarization_show(struct sys_device *dev, char *buf) +static ssize_t cpu_polarization_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { int cpu = dev->id; ssize_t count; @@ -950,7 +953,8 @@ static ssize_t cpu_polarization_show(struct sys_device *dev, char *buf) } static SYSDEV_ATTR(polarization, 0444, cpu_polarization_show, NULL); -static ssize_t show_cpu_address(struct sys_device *dev, char *buf) +static ssize_t show_cpu_address(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { return sprintf(buf, "%d\n", __cpu_logical_map[dev->id]); } @@ -970,7 +974,8 @@ static struct attribute_group cpu_common_attr_group = { .attrs = cpu_common_attrs, }; -static ssize_t show_capability(struct sys_device *dev, char *buf) +static ssize_t show_capability(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { unsigned int capability; int rc; @@ -982,7 +987,8 @@ static ssize_t show_capability(struct sys_device *dev, char *buf) } static SYSDEV_ATTR(capability, 0444, show_capability, NULL); -static ssize_t show_idle_count(struct sys_device *dev, char *buf) +static ssize_t show_idle_count(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct s390_idle_data *idle; unsigned long long idle_count; @@ -995,7 +1001,8 @@ static ssize_t show_idle_count(struct sys_device *dev, char *buf) } static SYSDEV_ATTR(idle_count, 0444, show_idle_count, NULL); -static ssize_t show_idle_time(struct sys_device *dev, char *buf) +static ssize_t show_idle_time(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct s390_idle_data *idle; unsigned long long new_time; @@ -1112,7 +1119,9 @@ out: return rc; } -static ssize_t __ref rescan_store(struct sys_device *dev, const char *buf, +static ssize_t __ref rescan_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { int rc; @@ -1123,7 +1132,9 @@ static ssize_t __ref rescan_store(struct sys_device *dev, const char *buf, static SYSDEV_ATTR(rescan, 0200, NULL, rescan_store); #endif /* CONFIG_HOTPLUG_CPU */ -static ssize_t dispatching_show(struct sys_device *dev, char *buf) +static ssize_t dispatching_show(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) { ssize_t count; @@ -1133,8 +1144,9 @@ static ssize_t dispatching_show(struct sys_device *dev, char *buf) return count; } -static ssize_t dispatching_store(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t dispatching_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) { int val, rc; char delim; diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index f2cede3947b2..ab70d9bd9261 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -1100,7 +1100,9 @@ static inline struct etr_aib *etr_aib_from_dev(struct sys_device *dev) return etr_port1_online ? &etr_port1 : NULL; } -static ssize_t etr_online_show(struct sys_device *dev, char *buf) +static ssize_t etr_online_show(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) { unsigned int online; @@ -1109,7 +1111,8 @@ static ssize_t etr_online_show(struct sys_device *dev, char *buf) } static ssize_t etr_online_store(struct sys_device *dev, - const char *buf, size_t count) + struct sysdev_attribute *attr, + const char *buf, size_t count) { unsigned int value; @@ -1136,7 +1139,9 @@ static ssize_t etr_online_store(struct sys_device *dev, static SYSDEV_ATTR(online, 0600, etr_online_show, etr_online_store); -static ssize_t etr_stepping_control_show(struct sys_device *dev, char *buf) +static ssize_t etr_stepping_control_show(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) { return sprintf(buf, "%i\n", (dev == &etr_port0_dev) ? etr_eacr.e0 : etr_eacr.e1); @@ -1144,7 +1149,8 @@ static ssize_t etr_stepping_control_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(stepping_control, 0400, etr_stepping_control_show, NULL); -static ssize_t etr_mode_code_show(struct sys_device *dev, char *buf) +static ssize_t etr_mode_code_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { if (!etr_port0_online && !etr_port1_online) /* Status word is not uptodate if both ports are offline. */ @@ -1155,7 +1161,8 @@ static ssize_t etr_mode_code_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(state_code, 0400, etr_mode_code_show, NULL); -static ssize_t etr_untuned_show(struct sys_device *dev, char *buf) +static ssize_t etr_untuned_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1166,7 +1173,8 @@ static ssize_t etr_untuned_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(untuned, 0400, etr_untuned_show, NULL); -static ssize_t etr_network_id_show(struct sys_device *dev, char *buf) +static ssize_t etr_network_id_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1177,7 +1185,8 @@ static ssize_t etr_network_id_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(network, 0400, etr_network_id_show, NULL); -static ssize_t etr_id_show(struct sys_device *dev, char *buf) +static ssize_t etr_id_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1188,7 +1197,8 @@ static ssize_t etr_id_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(id, 0400, etr_id_show, NULL); -static ssize_t etr_port_number_show(struct sys_device *dev, char *buf) +static ssize_t etr_port_number_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1199,7 +1209,8 @@ static ssize_t etr_port_number_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(port, 0400, etr_port_number_show, NULL); -static ssize_t etr_coupled_show(struct sys_device *dev, char *buf) +static ssize_t etr_coupled_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1210,7 +1221,8 @@ static ssize_t etr_coupled_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(coupled, 0400, etr_coupled_show, NULL); -static ssize_t etr_local_time_show(struct sys_device *dev, char *buf) +static ssize_t etr_local_time_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); @@ -1221,7 +1233,8 @@ static ssize_t etr_local_time_show(struct sys_device *dev, char *buf) static SYSDEV_ATTR(local_time, 0400, etr_local_time_show, NULL); -static ssize_t etr_utc_offset_show(struct sys_device *dev, char *buf) +static ssize_t etr_utc_offset_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct etr_aib *aib = etr_aib_from_dev(dev); diff --git a/arch/sh/drivers/dma/dma-sysfs.c b/arch/sh/drivers/dma/dma-sysfs.c index 51b57c0d1a3c..347ee11351ec 100644 --- a/arch/sh/drivers/dma/dma-sysfs.c +++ b/arch/sh/drivers/dma/dma-sysfs.c @@ -23,7 +23,8 @@ static struct sysdev_class dma_sysclass = { }; EXPORT_SYMBOL(dma_sysclass); -static ssize_t dma_show_devices(struct sys_device *dev, char *buf) +static ssize_t dma_show_devices(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t len = 0; int i; @@ -57,13 +58,15 @@ static int __init dma_sysclass_init(void) } postcore_initcall(dma_sysclass_init); -static ssize_t dma_show_dev_id(struct sys_device *dev, char *buf) +static ssize_t dma_show_dev_id(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct dma_channel *channel = to_dma_channel(dev); return sprintf(buf, "%s\n", channel->dev_id); } static ssize_t dma_store_dev_id(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct dma_channel *channel = to_dma_channel(dev); @@ -74,6 +77,7 @@ static ssize_t dma_store_dev_id(struct sys_device *dev, static SYSDEV_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id); static ssize_t dma_store_config(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct dma_channel *channel = to_dma_channel(dev); @@ -87,13 +91,15 @@ static ssize_t dma_store_config(struct sys_device *dev, static SYSDEV_ATTR(config, S_IWUSR, NULL, dma_store_config); -static ssize_t dma_show_mode(struct sys_device *dev, char *buf) +static ssize_t dma_show_mode(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct dma_channel *channel = to_dma_channel(dev); return sprintf(buf, "0x%08x\n", channel->mode); } static ssize_t dma_store_mode(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct dma_channel *channel = to_dma_channel(dev); @@ -104,7 +110,8 @@ static ssize_t dma_store_mode(struct sys_device *dev, static SYSDEV_ATTR(mode, S_IRUGO | S_IWUSR, dma_show_mode, dma_store_mode); #define dma_ro_attr(field, fmt) \ -static ssize_t dma_show_##field(struct sys_device *dev, char *buf) \ +static ssize_t dma_show_##field(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf)\ { \ struct dma_channel *channel = to_dma_channel(dev); \ return sprintf(buf, fmt, channel->field); \ diff --git a/arch/sparc64/kernel/sysfs.c b/arch/sparc64/kernel/sysfs.c index e885034a6b73..84e5ce146713 100644 --- a/arch/sparc64/kernel/sysfs.c +++ b/arch/sparc64/kernel/sysfs.c @@ -14,7 +14,8 @@ static DEFINE_PER_CPU(struct hv_mmu_statistics, mmu_stats) __attribute__((aligned(64))); #define SHOW_MMUSTAT_ULONG(NAME) \ -static ssize_t show_##NAME(struct sys_device *dev, char *buf) \ +static ssize_t show_##NAME(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ struct hv_mmu_statistics *p = &per_cpu(mmu_stats, dev->id); \ return sprintf(buf, "%lu\n", p->NAME); \ @@ -135,13 +136,16 @@ static unsigned long write_mmustat_enable(unsigned long val) return sun4v_mmustat_conf(ra, &orig_ra); } -static ssize_t show_mmustat_enable(struct sys_device *s, char *buf) +static ssize_t show_mmustat_enable(struct sys_device *s, + struct sysdev_attribute *attr, char *buf) { unsigned long val = run_on_cpu(s->id, read_mmustat_enable, 0); return sprintf(buf, "%lx\n", val); } -static ssize_t store_mmustat_enable(struct sys_device *s, const char *buf, size_t count) +static ssize_t store_mmustat_enable(struct sys_device *s, + struct sysdev_attribute *attr, const char *buf, + size_t count) { unsigned long val, err; int ret = sscanf(buf, "%ld", &val); @@ -179,14 +183,16 @@ static void unregister_mmu_stats(struct sys_device *s) #endif #define SHOW_CPUDATA_ULONG_NAME(NAME, MEMBER) \ -static ssize_t show_##NAME(struct sys_device *dev, char *buf) \ +static ssize_t show_##NAME(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ cpuinfo_sparc *c = &cpu_data(dev->id); \ return sprintf(buf, "%lu\n", c->MEMBER); \ } #define SHOW_CPUDATA_UINT_NAME(NAME, MEMBER) \ -static ssize_t show_##NAME(struct sys_device *dev, char *buf) \ +static ssize_t show_##NAME(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ cpuinfo_sparc *c = &cpu_data(dev->id); \ return sprintf(buf, "%u\n", c->MEMBER); \ diff --git a/arch/x86/kernel/cpu/mcheck/mce_64.c b/arch/x86/kernel/cpu/mcheck/mce_64.c index c4a7ec31394c..e6a4d5f67643 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_64.c +++ b/arch/x86/kernel/cpu/mcheck/mce_64.c @@ -762,10 +762,14 @@ DEFINE_PER_CPU(struct sys_device, device_mce); /* Why are there no generic functions for this? */ #define ACCESSOR(name, var, start) \ - static ssize_t show_ ## name(struct sys_device *s, char *buf) { \ + static ssize_t show_ ## name(struct sys_device *s, \ + struct sysdev_attribute *attr, \ + char *buf) { \ return sprintf(buf, "%lx\n", (unsigned long)var); \ } \ - static ssize_t set_ ## name(struct sys_device *s,const char *buf,size_t siz) { \ + static ssize_t set_ ## name(struct sys_device *s, \ + struct sysdev_attribute *attr, \ + const char *buf, size_t siz) { \ char *end; \ unsigned long new = simple_strtoul(buf, &end, 0); \ if (end == buf) return -EINVAL; \ @@ -786,14 +790,16 @@ ACCESSOR(bank3ctl,bank[3],mce_restart()) ACCESSOR(bank4ctl,bank[4],mce_restart()) ACCESSOR(bank5ctl,bank[5],mce_restart()) -static ssize_t show_trigger(struct sys_device *s, char *buf) +static ssize_t show_trigger(struct sys_device *s, struct sysdev_attribute *attr, + char *buf) { strcpy(buf, trigger); strcat(buf, "\n"); return strlen(trigger) + 1; } -static ssize_t set_trigger(struct sys_device *s,const char *buf,size_t siz) +static ssize_t set_trigger(struct sys_device *s, struct sysdev_attribute *attr, + const char *buf,size_t siz) { char *p; int len; diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c index 1f4cc48c14c6..d5ae2243f0b9 100644 --- a/arch/x86/kernel/cpu/mcheck/therm_throt.c +++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c @@ -35,6 +35,7 @@ atomic_t therm_throt_en = ATOMIC_INIT(0); #define define_therm_throt_sysdev_show_func(name) \ static ssize_t therm_throt_sysdev_show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ char *buf) \ { \ unsigned int cpu = dev->id; \ diff --git a/arch/x86/kernel/microcode.c b/arch/x86/kernel/microcode.c index 56b933119a04..fc4790638b69 100644 --- a/arch/x86/kernel/microcode.c +++ b/arch/x86/kernel/microcode.c @@ -644,7 +644,9 @@ static void microcode_fini_cpu(int cpu) mutex_unlock(µcode_mutex); } -static ssize_t reload_store(struct sys_device *dev, const char *buf, size_t sz) +static ssize_t reload_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t sz) { struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; char *end; @@ -674,14 +676,16 @@ static ssize_t reload_store(struct sys_device *dev, const char *buf, size_t sz) return sz; } -static ssize_t version_show(struct sys_device *dev, char *buf) +static ssize_t version_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; return sprintf(buf, "0x%x\n", uci->rev); } -static ssize_t pf_show(struct sys_device *dev, char *buf) +static ssize_t pf_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index e38dfed41d80..20537d507909 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -21,15 +21,16 @@ EXPORT_SYMBOL(cpu_sysdev_class); static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices); #ifdef CONFIG_HOTPLUG_CPU -static ssize_t show_online(struct sys_device *dev, char *buf) +static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id)); } -static ssize_t __ref store_online(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr, + const char *buf, size_t count) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); ssize_t ret; @@ -80,7 +81,8 @@ static inline void register_cpu_control(struct cpu *cpu) #ifdef CONFIG_KEXEC #include -static ssize_t show_crash_notes(struct sys_device *dev, char *buf) +static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { struct cpu *cpu = container_of(dev, struct cpu, sysdev); ssize_t rc; diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 937e8258981d..4d4e0e7b6e92 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -92,7 +92,8 @@ unregister_memory(struct memory_block *memory, struct mem_section *section) * uses. */ -static ssize_t show_mem_phys_index(struct sys_device *dev, char *buf) +static ssize_t show_mem_phys_index(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct memory_block *mem = container_of(dev, struct memory_block, sysdev); @@ -102,7 +103,8 @@ static ssize_t show_mem_phys_index(struct sys_device *dev, char *buf) /* * online, offline, going offline, etc. */ -static ssize_t show_mem_state(struct sys_device *dev, char *buf) +static ssize_t show_mem_state(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct memory_block *mem = container_of(dev, struct memory_block, sysdev); @@ -217,7 +219,8 @@ out: } static ssize_t -store_mem_state(struct sys_device *dev, const char *buf, size_t count) +store_mem_state(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct memory_block *mem; unsigned int phys_section_nr; @@ -248,7 +251,8 @@ out: * s.t. if I offline all of these sections I can then * remove the physical device? */ -static ssize_t show_phys_device(struct sys_device *dev, char *buf) +static ssize_t show_phys_device(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { struct memory_block *mem = container_of(dev, struct memory_block, sysdev); diff --git a/drivers/base/node.c b/drivers/base/node.c index 0f867a083338..5116b78c6325 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -36,11 +36,13 @@ static ssize_t node_read_cpumap(struct sys_device *dev, int type, char *buf) return len; } -static inline ssize_t node_read_cpumask(struct sys_device *dev, char *buf) +static inline ssize_t node_read_cpumask(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { return node_read_cpumap(dev, 0, buf); } -static inline ssize_t node_read_cpulist(struct sys_device *dev, char *buf) +static inline ssize_t node_read_cpulist(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { return node_read_cpumap(dev, 1, buf); } @@ -49,7 +51,8 @@ static SYSDEV_ATTR(cpumap, S_IRUGO, node_read_cpumask, NULL); static SYSDEV_ATTR(cpulist, S_IRUGO, node_read_cpulist, NULL); #define K(x) ((x) << (PAGE_SHIFT - 10)) -static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) +static ssize_t node_read_meminfo(struct sys_device * dev, + struct sysdev_attribute *attr, char * buf) { int n; int nid = dev->id; @@ -112,7 +115,8 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf) #undef K static SYSDEV_ATTR(meminfo, S_IRUGO, node_read_meminfo, NULL); -static ssize_t node_read_numastat(struct sys_device * dev, char * buf) +static ssize_t node_read_numastat(struct sys_device * dev, + struct sysdev_attribute *attr, char * buf) { return sprintf(buf, "numa_hit %lu\n" @@ -130,7 +134,8 @@ static ssize_t node_read_numastat(struct sys_device * dev, char * buf) } static SYSDEV_ATTR(numastat, S_IRUGO, node_read_numastat, NULL); -static ssize_t node_read_distance(struct sys_device * dev, char * buf) +static ssize_t node_read_distance(struct sys_device * dev, + struct sysdev_attribute *attr, char * buf) { int nid = dev->id; int len = 0; diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 50690d9df248..dc7dace14e1c 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -36,7 +36,7 @@ sysdev_show(struct kobject * kobj, struct attribute * attr, char * buffer) struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr); if (sysdev_attr->show) - return sysdev_attr->show(sysdev, buffer); + return sysdev_attr->show(sysdev, sysdev_attr, buffer); return -EIO; } @@ -49,7 +49,7 @@ sysdev_store(struct kobject * kobj, struct attribute * attr, struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr); if (sysdev_attr->store) - return sysdev_attr->store(sysdev, buffer, count); + return sysdev_attr->store(sysdev, sysdev_attr, buffer, count); return -EIO; } diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 3f6d9b0a6abe..199cd97e32e6 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -34,7 +34,8 @@ static SYSDEV_ATTR(_name, 0444, show_##_name, NULL) #define define_id_show_func(name) \ -static ssize_t show_##name(struct sys_device *dev, char *buf) \ +static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ unsigned int cpu = dev->id; \ return sprintf(buf, "%d\n", topology_##name(cpu)); \ @@ -59,14 +60,17 @@ static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf) #ifdef arch_provides_topology_pointers #define define_siblings_show_map(name) \ -static ssize_t show_##name(struct sys_device *dev, char *buf) \ +static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ unsigned int cpu = dev->id; \ return show_cpumap(0, &(topology_##name(cpu)), buf); \ } #define define_siblings_show_list(name) \ -static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \ +static ssize_t show_##name##_list(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ { \ unsigned int cpu = dev->id; \ return show_cpumap(1, &(topology_##name(cpu)), buf); \ @@ -74,7 +78,8 @@ static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \ #else #define define_siblings_show_map(name) \ -static ssize_t show_##name(struct sys_device *dev, char *buf) \ +static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, char *buf) \ { \ unsigned int cpu = dev->id; \ cpumask_t mask = topology_##name(cpu); \ @@ -82,7 +87,9 @@ static ssize_t show_##name(struct sys_device *dev, char *buf) \ } #define define_siblings_show_list(name) \ -static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \ +static ssize_t show_##name##_list(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ { \ unsigned int cpu = dev->id; \ cpumask_t mask = topology_##name(cpu); \ diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index e949618b9be0..31a0e0b455b6 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -21,7 +21,8 @@ static int __init cpuidle_sysfs_setup(char *unused) } __setup("cpuidle_sysfs_switch", cpuidle_sysfs_setup); -static ssize_t show_available_governors(struct sys_device *dev, char *buf) +static ssize_t show_available_governors(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t i = 0; struct cpuidle_governor *tmp; @@ -39,7 +40,8 @@ out: return i; } -static ssize_t show_current_driver(struct sys_device *dev, char *buf) +static ssize_t show_current_driver(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t ret; @@ -53,7 +55,8 @@ static ssize_t show_current_driver(struct sys_device *dev, char *buf) return ret; } -static ssize_t show_current_governor(struct sys_device *dev, char *buf) +static ssize_t show_current_governor(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t ret; @@ -68,6 +71,7 @@ static ssize_t show_current_governor(struct sys_device *dev, char *buf) } static ssize_t store_current_governor(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { char gov_name[CPUIDLE_NAME_LEN]; diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 591bc29b55f5..d4427cb86979 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -610,6 +610,7 @@ static ssize_t show_target_kb(struct sys_device *dev, char *buf) } static ssize_t store_target_kb(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { diff --git a/include/linux/sysdev.h b/include/linux/sysdev.h index f2767bc6b735..8dcf3162b21b 100644 --- a/include/linux/sysdev.h +++ b/include/linux/sysdev.h @@ -99,8 +99,9 @@ extern void sysdev_unregister(struct sys_device *); struct sysdev_attribute { struct attribute attr; - ssize_t (*show)(struct sys_device *, char *); - ssize_t (*store)(struct sys_device *, const char *, size_t); + ssize_t (*show)(struct sys_device *, struct sysdev_attribute *, char *); + ssize_t (*store)(struct sys_device *, struct sysdev_attribute *, + const char *, size_t); }; diff --git a/kernel/rtmutex-tester.c b/kernel/rtmutex-tester.c index 092e4c620af9..a56f629b057a 100644 --- a/kernel/rtmutex-tester.c +++ b/kernel/rtmutex-tester.c @@ -297,8 +297,8 @@ static int test_func(void *data) * * opcode:data */ -static ssize_t sysfs_test_command(struct sys_device *dev, const char *buf, - size_t count) +static ssize_t sysfs_test_command(struct sys_device *dev, struct sysdev_attribute *attr, + const char *buf, size_t count) { struct sched_param schedpar; struct test_thread_data *td; @@ -360,7 +360,8 @@ static ssize_t sysfs_test_command(struct sys_device *dev, const char *buf, * @dev: thread to query * @buf: char buffer to be filled with thread status info */ -static ssize_t sysfs_test_status(struct sys_device *dev, char *buf) +static ssize_t sysfs_test_status(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { struct test_thread_data *td; struct task_struct *tsk; diff --git a/kernel/sched.c b/kernel/sched.c index 99e6d850ecab..b1104ea5d255 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -7737,11 +7737,13 @@ static ssize_t sched_power_savings_store(const char *buf, size_t count, int smt) } #ifdef CONFIG_SCHED_MC -static ssize_t sched_mc_power_savings_show(struct sys_device *dev, char *page) +static ssize_t sched_mc_power_savings_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *page) { return sprintf(page, "%u\n", sched_mc_power_savings); } static ssize_t sched_mc_power_savings_store(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { return sched_power_savings_store(buf, count, 0); @@ -7751,11 +7753,13 @@ static SYSDEV_ATTR(sched_mc_power_savings, 0644, sched_mc_power_savings_show, #endif #ifdef CONFIG_SCHED_SMT -static ssize_t sched_smt_power_savings_show(struct sys_device *dev, char *page) +static ssize_t sched_smt_power_savings_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *page) { return sprintf(page, "%u\n", sched_smt_power_savings); } static ssize_t sched_smt_power_savings_store(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { return sched_power_savings_store(buf, count, 1); diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index dadde5361f32..b1c2da81b050 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -376,7 +376,8 @@ void clocksource_unregister(struct clocksource *cs) * Provides sysfs interface for listing current clocksource. */ static ssize_t -sysfs_show_current_clocksources(struct sys_device *dev, char *buf) +sysfs_show_current_clocksources(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) { ssize_t count = 0; @@ -397,6 +398,7 @@ sysfs_show_current_clocksources(struct sys_device *dev, char *buf) * clocksource selction. */ static ssize_t sysfs_override_clocksource(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t count) { struct clocksource *ovr = NULL; @@ -449,7 +451,9 @@ static ssize_t sysfs_override_clocksource(struct sys_device *dev, * Provides sysfs interface for listing registered clocksources */ static ssize_t -sysfs_show_available_clocksources(struct sys_device *dev, char *buf) +sysfs_show_available_clocksources(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) { struct clocksource *src; ssize_t count = 0; -- cgit From 9800794ac11d4646384b3a310dfd1fe2eed577bf Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Jul 2008 18:48:42 +0200 Subject: sysdev: Add utility functions for simple int/ulong variable sysdev attributes This adds a new sysdev_ext_attribute that stores a pointer to the variable it manages and some utility functions/macro to easily use them. Previously all users wrote custom macros to generate show/store functions for each variable, with this it is possible to avoid that in many cases. Signed-off-by: Andi Kleen Signed-off-by: Greg Kroah-Hartman --- drivers/base/sys.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysdev.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/sys.c b/drivers/base/sys.c index dc7dace14e1c..40fc14f03540 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -479,3 +479,52 @@ int __init system_bus_init(void) EXPORT_SYMBOL_GPL(sysdev_register); EXPORT_SYMBOL_GPL(sysdev_unregister); + +#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr) + +ssize_t sysdev_store_ulong(struct sys_device *sysdev, + struct sysdev_attribute *attr, + const char *buf, size_t size) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + char *end; + unsigned long new = simple_strtoul(buf, &end, 0); + if (end == buf) + return -EINVAL; + *(unsigned long *)(ea->var) = new; + return end - buf; +} +EXPORT_SYMBOL_GPL(sysdev_store_ulong); + +ssize_t sysdev_show_ulong(struct sys_device *sysdev, + struct sysdev_attribute *attr, + char *buf) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + return snprintf(buf, PAGE_SIZE, "%lx\n", *(unsigned long *)(ea->var)); +} +EXPORT_SYMBOL_GPL(sysdev_show_ulong); + +ssize_t sysdev_store_int(struct sys_device *sysdev, + struct sysdev_attribute *attr, + const char *buf, size_t size) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + char *end; + long new = simple_strtol(buf, &end, 0); + if (end == buf || new > INT_MAX || new < INT_MIN) + return -EINVAL; + *(int *)(ea->var) = new; + return end - buf; +} +EXPORT_SYMBOL_GPL(sysdev_store_int); + +ssize_t sysdev_show_int(struct sys_device *sysdev, + struct sysdev_attribute *attr, + char *buf) +{ + struct sysdev_ext_attribute *ea = to_ext_attr(attr); + return snprintf(buf, PAGE_SIZE, "%d\n", *(int *)(ea->var)); +} +EXPORT_SYMBOL_GPL(sysdev_show_int); + diff --git a/include/linux/sysdev.h b/include/linux/sysdev.h index 8dcf3162b21b..f395bb3fa2f2 100644 --- a/include/linux/sysdev.h +++ b/include/linux/sysdev.h @@ -119,4 +119,38 @@ struct sysdev_attribute { extern int sysdev_create_file(struct sys_device *, struct sysdev_attribute *); extern void sysdev_remove_file(struct sys_device *, struct sysdev_attribute *); +struct sysdev_ext_attribute { + struct sysdev_attribute attr; + void *var; +}; + +/* + * Support for simple variable sysdev attributes. + * The pointer to the variable is stored in a sysdev_ext_attribute + */ + +/* Add more types as needed */ + +extern ssize_t sysdev_show_ulong(struct sys_device *, struct sysdev_attribute *, + char *); +extern ssize_t sysdev_store_ulong(struct sys_device *, + struct sysdev_attribute *, const char *, size_t); +extern ssize_t sysdev_show_int(struct sys_device *, struct sysdev_attribute *, + char *); +extern ssize_t sysdev_store_int(struct sys_device *, + struct sysdev_attribute *, const char *, size_t); + +#define _SYSDEV_ULONG_ATTR(_name, _mode, _var) \ + { _SYSDEV_ATTR(_name, _mode, sysdev_show_ulong, sysdev_store_ulong), \ + &(_var) } +#define SYSDEV_ULONG_ATTR(_name, _mode, _var) \ + struct sysdev_ext_attribute attr_##_name = \ + _SYSDEV_ULONG_ATTR(_name, _mode, _var); +#define _SYSDEV_INT_ATTR(_name, _mode, _var) \ + { _SYSDEV_ATTR(_name, _mode, sysdev_show_int, sysdev_store_int), \ + &(_var) } +#define SYSDEV_INT_ATTR(_name, _mode, _var) \ + struct sysdev_ext_attribute attr_##_name = \ + _SYSDEV_INT_ATTR(_name, _mode, _var); + #endif /* _SYSDEV_H_ */ -- cgit From eadcf0d704a46979c29984fa05f1fc413c775bcb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 2 Jul 2008 12:46:22 -0700 Subject: MTD: handle pci_name() being const This changes the MTD core to handle pci_name() now returning a constant string. Cc: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/devices/block2mtd.c | 8 +++++--- include/linux/mtd/map.h | 2 +- include/linux/mtd/mtd.h | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 519d942e7940..7b72a1b36115 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -241,6 +241,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) { struct block_device *bdev; struct block2mtd_dev *dev; + char *name; if (!devname) return NULL; @@ -279,12 +280,13 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) /* Setup the MTD structure */ /* make the name contain the block device in */ - dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname), + name = kmalloc(sizeof("block2mtd: ") + strlen(devname) + 1, GFP_KERNEL); - if (!dev->mtd.name) + if (!name) goto devinit_err; - sprintf(dev->mtd.name, "block2mtd: %s", devname); + sprintf(name, "block2mtd: %s", devname); + dev->mtd.name = name; dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; dev->mtd.erasesize = erase_size; diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index a9fae032ba81..9c1d95491f8b 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -189,7 +189,7 @@ typedef union { */ struct map_info { - char *name; + const char *name; unsigned long size; resource_size_t phys; #define NO_XIP (-1UL) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 245f9098e171..8b5d49133ec6 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -121,7 +121,7 @@ struct mtd_info { u_int32_t oobavail; // Available OOB bytes per block // Kernel-only stuff starts here. - char *name; + const char *name; int index; /* ecc layout structure pointer - read only ! */ -- cgit From af5406895a05720a879dc33e2f4878fa503e81b3 Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Tue, 22 Jul 2008 19:24:26 -0500 Subject: module: reorder struct module to save space on 64 bit builds reorder struct module to save space on 64 bit builds. saves 1 cacheline_size (128 on default x86_64 & 64 on AMD Opteron/athlon) when CONFIG_MODULE_UNLOAD=y. Signed-off-by: Richard Kennedy Signed-off-by: Rusty Russell --- include/linux/module.h | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/include/linux/module.h b/include/linux/module.h index 3e03b1acbc94..63f0eb69e214 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -249,27 +249,28 @@ struct module /* Exported symbols */ const struct kernel_symbol *syms; - unsigned int num_syms; const unsigned long *crcs; + unsigned int num_syms; /* GPL-only exported symbols. */ - const struct kernel_symbol *gpl_syms; unsigned int num_gpl_syms; + const struct kernel_symbol *gpl_syms; const unsigned long *gpl_crcs; /* unused exported symbols. */ const struct kernel_symbol *unused_syms; - unsigned int num_unused_syms; const unsigned long *unused_crcs; + unsigned int num_unused_syms; + /* GPL-only, unused exported symbols. */ - const struct kernel_symbol *unused_gpl_syms; unsigned int num_unused_gpl_syms; + const struct kernel_symbol *unused_gpl_syms; const unsigned long *unused_gpl_crcs; /* symbols that will be GPL-only in the near future. */ const struct kernel_symbol *gpl_future_syms; - unsigned int num_gpl_future_syms; const unsigned long *gpl_future_crcs; + unsigned int num_gpl_future_syms; /* Exception table */ unsigned int num_exentries; @@ -300,23 +301,9 @@ struct module #ifdef CONFIG_GENERIC_BUG /* Support for BUG */ + unsigned num_bugs; struct list_head bug_list; struct bug_entry *bug_table; - unsigned num_bugs; -#endif - -#ifdef CONFIG_MODULE_UNLOAD - /* Reference counts */ - struct module_ref ref[NR_CPUS]; - - /* What modules depend on me? */ - struct list_head modules_which_use_me; - - /* Who is waiting for us to be unloaded */ - struct task_struct *waiter; - - /* Destruction function. */ - void (*exit)(void); #endif #ifdef CONFIG_KALLSYMS @@ -342,6 +329,21 @@ struct module struct marker *markers; unsigned int num_markers; #endif + +#ifdef CONFIG_MODULE_UNLOAD + /* What modules depend on me? */ + struct list_head modules_which_use_me; + + /* Who is waiting for us to be unloaded */ + struct task_struct *waiter; + + /* Destruction function. */ + void (*exit)(void); + + /* Reference counts */ + struct module_ref ref[NR_CPUS]; +#endif + }; #ifndef MODULE_ARCH_INIT #define MODULE_ARCH_INIT {} -- cgit From f7f5b67557eac1131ba6532522e3c50eced34238 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 22 Jul 2008 19:24:26 -0500 Subject: Shrink struct module: CONFIG_UNUSED_SYMBOLS ifdefs module.c and module.h conatains code for finding exported symbols which are declared with EXPORT_UNUSED_SYMBOL, and this code is compiled in even if CONFIG_UNUSED_SYMBOLS is not set and thus there can be no EXPORT_UNUSED_SYMBOLs in modules anyway (because EXPORT_UNUSED_SYMBOL(x) are compiled out to nothing then). This patch adds required #ifdefs. Signed-off-by: Denys Vlasenko Signed-off-by: Rusty Russell --- include/linux/module.h | 2 ++ init/Kconfig | 4 ++-- kernel/module.c | 49 ++++++++++++++++++++++++++++++++++--------------- 3 files changed, 38 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/module.h b/include/linux/module.h index 63f0eb69e214..a860a2c1f379 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -257,6 +257,7 @@ struct module const struct kernel_symbol *gpl_syms; const unsigned long *gpl_crcs; +#ifdef CONFIG_UNUSED_SYMBOLS /* unused exported symbols. */ const struct kernel_symbol *unused_syms; const unsigned long *unused_crcs; @@ -266,6 +267,7 @@ struct module unsigned int num_unused_gpl_syms; const struct kernel_symbol *unused_gpl_syms; const unsigned long *unused_gpl_crcs; +#endif /* symbols that will be GPL-only in the near future. */ const struct kernel_symbol *gpl_future_syms; diff --git a/init/Kconfig b/init/Kconfig index 6199d1120900..c8578f9ee31d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -856,8 +856,8 @@ config MODULE_UNLOAD help Without this option you will not be able to unload any modules (note that some modules may not be unloadable - anyway), which makes your kernel slightly smaller and - simpler. If unsure, say Y. + anyway), which makes your kernel smaller, faster + and simpler. If unsure, say Y. config MODULE_FORCE_UNLOAD bool "Forced module unloading" diff --git a/kernel/module.c b/kernel/module.c index c51c089c666e..ea9580521eb1 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -134,17 +134,19 @@ extern const struct kernel_symbol __start___ksymtab_gpl[]; extern const struct kernel_symbol __stop___ksymtab_gpl[]; extern const struct kernel_symbol __start___ksymtab_gpl_future[]; extern const struct kernel_symbol __stop___ksymtab_gpl_future[]; -extern const struct kernel_symbol __start___ksymtab_unused[]; -extern const struct kernel_symbol __stop___ksymtab_unused[]; -extern const struct kernel_symbol __start___ksymtab_unused_gpl[]; -extern const struct kernel_symbol __stop___ksymtab_unused_gpl[]; extern const struct kernel_symbol __start___ksymtab_gpl_future[]; extern const struct kernel_symbol __stop___ksymtab_gpl_future[]; extern const unsigned long __start___kcrctab[]; extern const unsigned long __start___kcrctab_gpl[]; extern const unsigned long __start___kcrctab_gpl_future[]; +#ifdef CONFIG_UNUSED_SYMBOLS +extern const struct kernel_symbol __start___ksymtab_unused[]; +extern const struct kernel_symbol __stop___ksymtab_unused[]; +extern const struct kernel_symbol __start___ksymtab_unused_gpl[]; +extern const struct kernel_symbol __stop___ksymtab_unused_gpl[]; extern const unsigned long __start___kcrctab_unused[]; extern const unsigned long __start___kcrctab_unused_gpl[]; +#endif #ifndef CONFIG_MODVERSIONS #define symversion(base, idx) NULL @@ -198,12 +200,14 @@ static bool each_symbol(bool (*fn)(const struct symsearch *arr, { __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future, __start___kcrctab_gpl_future, WILL_BE_GPL_ONLY, false }, +#ifdef CONFIG_UNUSED_SYMBOLS { __start___ksymtab_unused, __stop___ksymtab_unused, __start___kcrctab_unused, NOT_GPL_ONLY, true }, { __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl, __start___kcrctab_unused_gpl, GPL_ONLY, true }, +#endif }; if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data)) @@ -220,6 +224,7 @@ static bool each_symbol(bool (*fn)(const struct symsearch *arr, mod->gpl_future_syms + mod->num_gpl_future_syms, mod->gpl_future_crcs, WILL_BE_GPL_ONLY, false }, +#ifdef CONFIG_UNUSED_SYMBOLS { mod->unused_syms, mod->unused_syms + mod->num_unused_syms, mod->unused_crcs, @@ -228,6 +233,7 @@ static bool each_symbol(bool (*fn)(const struct symsearch *arr, mod->unused_gpl_syms + mod->num_unused_gpl_syms, mod->unused_gpl_crcs, GPL_ONLY, true }, +#endif }; if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data)) @@ -270,6 +276,7 @@ static bool find_symbol_in_section(const struct symsearch *syms, } } +#ifdef CONFIG_UNUSED_SYMBOLS if (syms->unused && fsa->warn) { printk(KERN_WARNING "Symbol %s is marked as UNUSED, " "however this module is using it.\n", fsa->name); @@ -281,6 +288,7 @@ static bool find_symbol_in_section(const struct symsearch *syms, "mailinglist together with submitting your code for " "inclusion.\n"); } +#endif fsa->owner = owner; fsa->crc = symversion(syms->crcs, symnum); @@ -1476,8 +1484,10 @@ static int verify_export_symbols(struct module *mod) { mod->syms, mod->num_syms }, { mod->gpl_syms, mod->num_gpl_syms }, { mod->gpl_future_syms, mod->num_gpl_future_syms }, +#ifdef CONFIG_UNUSED_SYMBOLS { mod->unused_syms, mod->num_unused_syms }, { mod->unused_gpl_syms, mod->num_unused_gpl_syms }, +#endif }; for (i = 0; i < ARRAY_SIZE(arr); i++) { @@ -1795,10 +1805,12 @@ static struct module *load_module(void __user *umod, unsigned int gplfutureindex; unsigned int gplfuturecrcindex; unsigned int unwindex = 0; +#ifdef CONFIG_UNUSED_SYMBOLS unsigned int unusedindex; unsigned int unusedcrcindex; unsigned int unusedgplindex; unsigned int unusedgplcrcindex; +#endif unsigned int markersindex; unsigned int markersstringsindex; struct module *mod; @@ -1881,13 +1893,15 @@ static struct module *load_module(void __user *umod, exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab"); gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl"); gplfutureindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl_future"); - unusedindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused"); - unusedgplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused_gpl"); crcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab"); gplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl"); gplfuturecrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl_future"); +#ifdef CONFIG_UNUSED_SYMBOLS + unusedindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused"); + unusedgplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused_gpl"); unusedcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused"); unusedgplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused_gpl"); +#endif setupindex = find_sec(hdr, sechdrs, secstrings, "__param"); exindex = find_sec(hdr, sechdrs, secstrings, "__ex_table"); obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm"); @@ -2049,14 +2063,15 @@ static struct module *load_module(void __user *umod, mod->gpl_crcs = (void *)sechdrs[gplcrcindex].sh_addr; mod->num_gpl_future_syms = sechdrs[gplfutureindex].sh_size / sizeof(*mod->gpl_future_syms); - mod->num_unused_syms = sechdrs[unusedindex].sh_size / - sizeof(*mod->unused_syms); - mod->num_unused_gpl_syms = sechdrs[unusedgplindex].sh_size / - sizeof(*mod->unused_gpl_syms); mod->gpl_future_syms = (void *)sechdrs[gplfutureindex].sh_addr; if (gplfuturecrcindex) mod->gpl_future_crcs = (void *)sechdrs[gplfuturecrcindex].sh_addr; +#ifdef CONFIG_UNUSED_SYMBOLS + mod->num_unused_syms = sechdrs[unusedindex].sh_size / + sizeof(*mod->unused_syms); + mod->num_unused_gpl_syms = sechdrs[unusedgplindex].sh_size / + sizeof(*mod->unused_gpl_syms); mod->unused_syms = (void *)sechdrs[unusedindex].sh_addr; if (unusedcrcindex) mod->unused_crcs = (void *)sechdrs[unusedcrcindex].sh_addr; @@ -2064,13 +2079,17 @@ static struct module *load_module(void __user *umod, if (unusedgplcrcindex) mod->unused_gpl_crcs = (void *)sechdrs[unusedgplcrcindex].sh_addr; +#endif #ifdef CONFIG_MODVERSIONS - if ((mod->num_syms && !crcindex) || - (mod->num_gpl_syms && !gplcrcindex) || - (mod->num_gpl_future_syms && !gplfuturecrcindex) || - (mod->num_unused_syms && !unusedcrcindex) || - (mod->num_unused_gpl_syms && !unusedgplcrcindex)) { + if ((mod->num_syms && !crcindex) + || (mod->num_gpl_syms && !gplcrcindex) + || (mod->num_gpl_future_syms && !gplfuturecrcindex) +#ifdef CONFIG_UNUSED_SYMBOLS + || (mod->num_unused_syms && !unusedcrcindex) + || (mod->num_unused_gpl_syms && !unusedgplcrcindex) +#endif + ) { printk(KERN_WARNING "%s: No versions for exported symbols.\n", mod->name); err = try_to_force_load(mod, "nocrc"); if (err) -- cgit From 2f0f2a334bc38b61a9afca951185cd3844ee709d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 22 Jul 2008 19:24:27 -0500 Subject: module: turn longs into ints for module sizes This shrinks module.o and each *.ko file. And finally, structure members which hold length of module code (four such members there) and count of symbols are converted from longs to ints. We cannot possibly have a module where 32 bits won't be enough to hold such counts. For one, module loading checks module size for sanity before loading, so such insanely big module will fail that test first. Signed-off-by: Denys Vlasenko Signed-off-by: Rusty Russell --- include/linux/module.h | 6 +++--- kernel/module.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/module.h b/include/linux/module.h index a860a2c1f379..fce15ebd0e1c 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -288,10 +288,10 @@ struct module void *module_core; /* Here are the sizes of the init and core sections */ - unsigned long init_size, core_size; + unsigned int init_size, core_size; /* The size of the executable code in each section. */ - unsigned long init_text_size, core_text_size; + unsigned int init_text_size, core_text_size; /* The handle returned from unwind_add_table. */ void *unwind_info; @@ -311,7 +311,7 @@ struct module #ifdef CONFIG_KALLSYMS /* We keep the symbol and string tables for kallsyms. */ Elf_Sym *symtab; - unsigned long num_symtab; + unsigned int num_symtab; char *strtab; /* Section attributes */ diff --git a/kernel/module.c b/kernel/module.c index ea9580521eb1..5c7eb0695b3c 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1567,7 +1567,7 @@ static int simplify_symbols(Elf_Shdr *sechdrs, } /* Update size with this section: return offset. */ -static long get_offset(unsigned long *size, Elf_Shdr *sechdr) +static long get_offset(unsigned int *size, Elf_Shdr *sechdr) { long ret; @@ -2562,7 +2562,7 @@ static int m_show(struct seq_file *m, void *p) struct module *mod = list_entry(p, struct module, list); char buf[8]; - seq_printf(m, "%s %lu", + seq_printf(m, "%s %u", mod->name, mod->init_size + mod->core_size); print_unload_info(m, mod); -- cgit From df648c9fbebb4de50e7a6e76cc253c7cb0421f9b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 8 Jul 2008 19:00:18 +0200 Subject: rework try_then_request_module to do less in non-modular kernels This reworks try_then_request_module to only invoke the "lookup" function "x" once when the kernel is not modular. Signed-off-by: Johannes Berg Signed-off-by: Rusty Russell --- include/linux/kmod.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 5dc13848891b..9bdb28d6660f 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -29,11 +29,12 @@ /* modprobe exit status on success, -ve on error. Return value * usually useless though. */ extern int request_module(const char * name, ...) __attribute__ ((format (printf, 1, 2))); +#define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x))) #else static inline int request_module(const char * name, ...) { return -ENOSYS; } +#define try_then_request_module(x, mod...) (x) #endif -#define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x))) struct key; struct file; -- cgit From a1ef5adb4cad43460ebba23c5a78cf4a55bb6a5b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 8 Jul 2008 19:00:17 +0200 Subject: remove CONFIG_KMOD from core kernel code Always compile request_module when the kernel allows modules. Signed-off-by: Johannes Berg Signed-off-by: Rusty Russell --- include/linux/kmod.h | 2 +- kernel/exec_domain.c | 2 +- kernel/kmod.c | 2 +- kernel/sysctl.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 9bdb28d6660f..0509c4ce4857 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -25,7 +25,7 @@ #define KMOD_PATH_LEN 256 -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES /* modprobe exit status on success, -ve on error. Return value * usually useless though. */ extern int request_module(const char * name, ...) __attribute__ ((format (printf, 1, 2))); diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c index a9e6bad9f706..c1ef192aa655 100644 --- a/kernel/exec_domain.c +++ b/kernel/exec_domain.c @@ -65,7 +65,7 @@ lookup_exec_domain(u_long personality) goto out; } -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES read_unlock(&exec_domains_lock); request_module("personality-%ld", pers); read_lock(&exec_domains_lock); diff --git a/kernel/kmod.c b/kernel/kmod.c index 8df97d3dfda8..90d7af1c1655 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -42,7 +42,7 @@ extern int max_threads; static struct workqueue_struct *khelper_wq; -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES /* modprobe_path is set via /proc/sys. diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 6b16e16428d8..b859e6b5a767 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -110,7 +110,7 @@ static int min_percpu_pagelist_fract = 8; static int ngroups_max = NGROUPS_MAX; -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES extern char modprobe_path[]; #endif #ifdef CONFIG_CHR_DEV_SG @@ -475,7 +475,7 @@ static struct ctl_table kern_table[] = { .proc_handler = &ftrace_enable_sysctl, }, #endif -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES { .ctl_name = KERN_MODPROBE, .procname = "modprobe", -- cgit From 651910874633a75f4a726d44e449be0a56b4b2e0 Mon Sep 17 00:00:00 2001 From: John Reiser Date: Mon, 21 Jul 2008 14:21:32 -0700 Subject: execve filename: document and export via auxiliary vector The Linux kernel puts the filename argument of execve() into the new address space. Many developers are surprised to learn this. Those who know and could use it, object "But it's not documented." Those who want to use it dislike the expression (char *)(1+ strlen(env[-1+ n_env]) + env[-1+ n_env]) because it requires locating the last original environment variable, and assumes that the filename follows the characters. This patch documents the insertion of the filename, and makes it easier to find by adding a new tag AT_EXECFN in the ElfXX_auxv_t; see . In many cases readlink("/proc/self/exe",) gives the same answer. But if all the original pages get unmapped, then the kernel erases the symlink for /proc/self/exe. This can happen when a program decompressor does a good job of cleaning up after uncompressing directly to memory, so that the address space of the target program looks the same as if compression had never happened. One example is http://upx.sourceforge.net . One notable use of the underlying concept (what path containED the executable) is glibc expanding $ORIGIN in DT_RUNPATH. In practice for the near term, it may be a good idea for user-mode code to use both /proc/self/exe and AT_EXECFN as fall-back methods for each other. /proc/self/exe can fail due to unmapping, AT_EXECFN can fail because it won't be present on non-new systems. The auxvec or {AT_EXECFN}.d_val also can get overwritten, although in nearly all cases this would be the result of a bug. The runtime cost is one NEW_AUX_ENT using two words of stack space. The underlying value is maintained already as bprm->exec; setup_arg_pages() in fs/exec.c slides it for stack_shift, etc. Signed-off-by: John Reiser Cc: Roland McGrath Cc: Jakub Jelinek Cc: Ulrich Drepper Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_elf.c | 1 + include/linux/auxvec.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index d48ff5f370f4..639d2d8b5710 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -204,6 +204,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, NEW_AUX_ENT(AT_GID, tsk->gid); NEW_AUX_ENT(AT_EGID, tsk->egid); NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm)); + NEW_AUX_ENT(AT_EXECFN, bprm->exec); if (k_platform) { NEW_AUX_ENT(AT_PLATFORM, (elf_addr_t)(unsigned long)u_platform); diff --git a/include/linux/auxvec.h b/include/linux/auxvec.h index ad895455ab72..0da17d14fd13 100644 --- a/include/linux/auxvec.h +++ b/include/linux/auxvec.h @@ -26,8 +26,10 @@ #define AT_SECURE 23 /* secure mode boolean */ +#define AT_EXECFN 31 /* filename of program */ #ifdef __KERNEL__ -#define AT_VECTOR_SIZE_BASE (14 + 2) /* NEW_AUX_ENT entries in auxiliary table */ +#define AT_VECTOR_SIZE_BASE 17 /* NEW_AUX_ENT entries in auxiliary table */ + /* number of "#define AT_.*" above, minus {AT_NULL, AT_IGNORE, AT_NOTELF} */ #endif #endif /* _LINUX_AUXVEC_H */ -- cgit From 0c36ec31473593aa937ff04f3b3b630e81512734 Mon Sep 17 00:00:00 2001 From: Juergen Beisert Date: Mon, 21 Jul 2008 14:21:34 -0700 Subject: gpio: gpio driver for max7301 SPI GPIO expander Maxim's MAX7301 is an SPI GPIO expander with 28 GPIOs. Note: MAX7301's interrupt feature is not supported yet. [akpm@linux-foundation.org: coding-style fixes] [g.liakhovetski@pengutronix.de: Fix inaccuracies in comments, check spi_setup() return code, mask off high byte in max7301_read()] Signed-off-by: Juergen Beisert Signed-off-by: Guennadi Liakhovetski Cc: Russell King Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/max7301.c | 339 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/max7301.h | 9 ++ 4 files changed, 355 insertions(+) create mode 100644 drivers/gpio/max7301.c create mode 100644 include/linux/spi/max7301.h (limited to 'include/linux') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 008c38ba774f..9e0c4fbfc51a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -69,6 +69,12 @@ config GPIO_PCF857X comment "SPI GPIO expanders:" +config GPIO_MAX7301 + tristate "Maxim MAX7301 GPIO expander" + depends on SPI_MASTER + help + gpio driver for Maxim MAX7301 SPI GPIO expander. + config GPIO_MCP23S08 tristate "Microchip MCP23S08 I/O expander" depends on SPI_MASTER diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fdde9923cf33..16e796dc5410 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG obj-$(CONFIG_HAVE_GPIO_LIB) += gpiolib.o +obj-$(CONFIG_GPIO_MAX7301) += max7301.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o diff --git a/drivers/gpio/max7301.c b/drivers/gpio/max7301.c new file mode 100644 index 000000000000..39c795ad8312 --- /dev/null +++ b/drivers/gpio/max7301.c @@ -0,0 +1,339 @@ +/** + * drivers/gpio/max7301.c + * + * Copyright (C) 2006 Juergen Beisert, Pengutronix + * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix + * + * 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. + * + * The Maxim's MAX7301 device is an SPI driven GPIO expander. There are + * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more + * details + * Note: + * - DIN must be stable at the rising edge of clock. + * - when writing: + * - always clock in 16 clocks at once + * - at DIN: D15 first, D0 last + * - D0..D7 = databyte, D8..D14 = commandbyte + * - D15 = low -> write command + * - when reading + * - always clock in 16 clocks at once + * - at DIN: D15 first, D0 last + * - D0..D7 = dummy, D8..D14 = register address + * - D15 = high -> read command + * - raise CS and assert it again + * - always clock in 16 clocks at once + * - at DOUT: D15 first, D0 last + * - D0..D7 contains the data from the first cycle + * + * The driver exports a standard gpiochip interface + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "max7301" + +/* + * Pin configurations, see MAX7301 datasheet page 6 + */ +#define PIN_CONFIG_MASK 0x03 +#define PIN_CONFIG_IN_PULLUP 0x03 +#define PIN_CONFIG_IN_WO_PULLUP 0x02 +#define PIN_CONFIG_OUT 0x01 + +#define PIN_NUMBER 28 + + +/* + * Some registers must be read back to modify. + * To save time we cache them here in memory + */ +struct max7301 { + struct mutex lock; + u8 port_config[8]; /* field 0 is unused */ + u32 out_level; /* cached output levels */ + struct gpio_chip chip; + struct spi_device *spi; +}; + +/** + * max7301_write - Write a new register content + * @spi: The SPI device + * @reg: Register offset + * @val: Value to write + * + * A write to the MAX7301 means one message with one transfer + * + * Returns 0 if successful or a negative value on error + */ +static int max7301_write(struct spi_device *spi, unsigned int reg, unsigned int val) +{ + u16 word = ((reg & 0x7F) << 8) | (val & 0xFF); + return spi_write(spi, (const u8 *)&word, sizeof(word)); +} + +/** + * max7301_read - Read back register content + * @spi: The SPI device + * @reg: Register offset + * + * A read from the MAX7301 means two transfers; here, one message each + * + * Returns positive 8 bit value from device if successful or a + * negative value on error + */ +static int max7301_read(struct spi_device *spi, unsigned int reg) +{ + int ret; + u16 word; + + word = 0x8000 | (reg << 8); + ret = spi_write(spi, (const u8 *)&word, sizeof(word)); + if (ret) + return ret; + /* + * This relies on the fact, that a transfer with NULL tx_buf shifts out + * zero bytes (=NOOP for MAX7301) + */ + ret = spi_read(spi, (u8 *)&word, sizeof(word)); + if (ret) + return ret; + return word & 0xff; +} + +static int max7301_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + u8 *config; + int ret; + + /* First 4 pins are unused in the controller */ + offset += 4; + + config = &ts->port_config[offset >> 2]; + + mutex_lock(&ts->lock); + + /* Standard GPIO API doesn't support pull-ups, has to be extended. + * Hard-coding no pollup for now. */ + *config = (*config & ~(3 << (offset & 3))) | (1 << (offset & 3)); + + ret = max7301_write(ts->spi, 0x08 + (offset >> 2), *config); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int __max7301_set(struct max7301 *ts, unsigned offset, int value) +{ + if (value) { + ts->out_level |= 1 << offset; + return max7301_write(ts->spi, 0x20 + offset, 0x01); + } else { + ts->out_level &= ~(1 << offset); + return max7301_write(ts->spi, 0x20 + offset, 0x00); + } +} + +static int max7301_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + u8 *config; + int ret; + + /* First 4 pins are unused in the controller */ + offset += 4; + + config = &ts->port_config[offset >> 2]; + + mutex_lock(&ts->lock); + + *config = (*config & ~(3 << (offset & 3))) | (1 << (offset & 3)); + + ret = __max7301_set(ts, offset, value); + + if (!ret) + ret = max7301_write(ts->spi, 0x08 + (offset >> 2), *config); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int max7301_get(struct gpio_chip *chip, unsigned offset) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + int config, level = -EINVAL; + + /* First 4 pins are unused in the controller */ + offset += 4; + + mutex_lock(&ts->lock); + + config = (ts->port_config[offset >> 2] >> ((offset & 3) * 2)) & 3; + + switch (config) { + case 1: + /* Output: return cached level */ + level = !!(ts->out_level & (1 << offset)); + break; + case 2: + case 3: + /* Input: read out */ + level = max7301_read(ts->spi, 0x20 + offset) & 0x01; + } + mutex_unlock(&ts->lock); + + return level; +} + +static void max7301_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + + /* First 4 pins are unused in the controller */ + offset += 4; + + mutex_lock(&ts->lock); + + __max7301_set(ts, offset, value); + + mutex_unlock(&ts->lock); +} + +static int __devinit max7301_probe(struct spi_device *spi) +{ + struct max7301 *ts; + struct max7301_platform_data *pdata; + int i, ret; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->base) + return -ENODEV; + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 16; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + ts = kzalloc(sizeof(struct max7301), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + mutex_init(&ts->lock); + + dev_set_drvdata(&spi->dev, ts); + + /* Power up the chip and disable IRQ output */ + max7301_write(spi, 0x04, 0x01); + + ts->spi = spi; + + ts->chip.label = DRIVER_NAME, + + ts->chip.direction_input = max7301_direction_input; + ts->chip.get = max7301_get; + ts->chip.direction_output = max7301_direction_output; + ts->chip.set = max7301_set; + + ts->chip.base = pdata->base; + ts->chip.ngpio = PIN_NUMBER; + ts->chip.can_sleep = 1; + ts->chip.dev = &spi->dev; + ts->chip.owner = THIS_MODULE; + + ret = gpiochip_add(&ts->chip); + if (ret) + goto exit_destroy; + + /* + * tristate all pins in hardware and cache the + * register values for later use. + */ + for (i = 1; i < 8; i++) { + int j; + /* 0xAA means input with internal pullup disabled */ + max7301_write(spi, 0x08 + i, 0xAA); + ts->port_config[i] = 0xAA; + for (j = 0; j < 4; j++) { + int idx = ts->chip.base + (i - 1) * 4 + j; + ret = gpio_direction_input(idx); + if (ret) + goto exit_remove; + gpio_free(idx); + } + } + return ret; + +exit_remove: + gpiochip_remove(&ts->chip); +exit_destroy: + dev_set_drvdata(&spi->dev, NULL); + mutex_destroy(&ts->lock); + kfree(ts); + return ret; +} + +static int max7301_remove(struct spi_device *spi) +{ + struct max7301 *ts; + int ret; + + ts = dev_get_drvdata(&spi->dev); + if (ts == NULL) + return -ENODEV; + + dev_set_drvdata(&spi->dev, NULL); + + /* Power down the chip and disable IRQ output */ + max7301_write(spi, 0x04, 0x00); + + ret = gpiochip_remove(&ts->chip); + if (!ret) { + mutex_destroy(&ts->lock); + kfree(ts); + } else + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", + ret); + + return ret; +} + +static struct spi_driver max7301_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = max7301_probe, + .remove = __devexit_p(max7301_remove), +}; + +static int __init max7301_init(void) +{ + return spi_register_driver(&max7301_driver); +} + +static void __exit max7301_exit(void) +{ + spi_unregister_driver(&max7301_driver); +} + +module_init(max7301_init); +module_exit(max7301_exit); + +MODULE_AUTHOR("Juergen Beisert"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MAX7301 SPI based GPIO-Expander"); diff --git a/include/linux/spi/max7301.h b/include/linux/spi/max7301.h new file mode 100644 index 000000000000..6dfd83f19b4b --- /dev/null +++ b/include/linux/spi/max7301.h @@ -0,0 +1,9 @@ +#ifndef LINUX_SPI_MAX7301_H +#define LINUX_SPI_MAX7301_H + +struct max7301_platform_data { + /* number assigned to the first GPIO */ + unsigned base; +}; + +#endif -- cgit From 95da310e66ee8090119596c70ca8432e57f9a97f Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 22 Jul 2008 11:09:07 +0100 Subject: usb_serial: API all change USB serial likes to use port->tty back pointers for the real work it does and to do so without any actual locking. Unfortunately when you consider hangup events, hangup/parallel reopen or even worse hangup followed by parallel close events the tty->port and port->tty pointers are not guaranteed to be the same as port->tty is the active tty while tty->port is the port the tty may or may not still be attached to. So rework the entire API to pass the tty struct. For console cases we need to pass both for now. This shows up multiple drivers that immediately crash with USB console some of which have been fixed in the process. Longer term we need a proper tty as console abstraction Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds --- drivers/usb/serial/aircable.c | 19 +- drivers/usb/serial/airprime.c | 355 ++++++++++++++++++++++++++++++++++ drivers/usb/serial/ark3116.c | 32 +-- drivers/usb/serial/belkin_sa.c | 69 +++---- drivers/usb/serial/ch341.c | 10 +- drivers/usb/serial/console.c | 34 ++-- drivers/usb/serial/cp2101.c | 74 +++---- drivers/usb/serial/cyberjack.c | 47 +++-- drivers/usb/serial/cypress_m8.c | 117 ++++++----- drivers/usb/serial/digi_acceleport.c | 127 ++++++------ drivers/usb/serial/empeg.c | 78 +++----- drivers/usb/serial/ftdi_sio.c | 211 ++++++++++---------- drivers/usb/serial/garmin_gps.c | 58 ++---- drivers/usb/serial/generic.c | 39 ++-- drivers/usb/serial/io_edgeport.c | 175 ++++++----------- drivers/usb/serial/io_ti.c | 109 ++++++----- drivers/usb/serial/ipaq.c | 48 +++-- drivers/usb/serial/ipw.c | 15 +- drivers/usb/serial/ir-usb.c | 81 ++++---- drivers/usb/serial/iuu_phoenix.c | 43 ++-- drivers/usb/serial/keyspan.c | 138 ++++++------- drivers/usb/serial/keyspan.h | 39 ++-- drivers/usb/serial/keyspan_pda.c | 69 +++---- drivers/usb/serial/kl5kusb105.c | 74 ++++--- drivers/usb/serial/kobil_sct.c | 80 ++++---- drivers/usb/serial/mct_u232.c | 121 +++++------- drivers/usb/serial/mos7720.c | 87 ++++----- drivers/usb/serial/mos7840.c | 165 +++++++--------- drivers/usb/serial/navman.c | 10 +- drivers/usb/serial/omninet.c | 26 +-- drivers/usb/serial/option.c | 105 ++++------ drivers/usb/serial/oti6858.c | 119 +++++------- drivers/usb/serial/pl2303.c | 76 ++++---- drivers/usb/serial/safe_serial.c | 14 +- drivers/usb/serial/sierra.c | 93 +++------ drivers/usb/serial/spcp8x5.c | 69 ++++--- drivers/usb/serial/ti_usb_3410_5052.c | 203 ++++++++++--------- drivers/usb/serial/usb-serial.c | 82 ++++---- drivers/usb/serial/usb_debug.c | 5 +- drivers/usb/serial/visor.c | 66 ++----- drivers/usb/serial/whiteheat.c | 102 +++++----- include/linux/usb/serial.h | 56 +++--- 42 files changed, 1820 insertions(+), 1720 deletions(-) create mode 100644 drivers/usb/serial/airprime.c (limited to 'include/linux') diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index db6f97a93c02..79ea98c66fa8 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -272,7 +272,7 @@ static void aircable_read(struct work_struct *work) * 64 bytes, to ensure I do not get throttled. * Ask USB mailing list for better aproach. */ - tty = port->tty; + tty = port->port.tty; if (!tty) { schedule_work(&priv->rx_work); @@ -378,13 +378,14 @@ static void aircable_shutdown(struct usb_serial *serial) } } -static int aircable_write_room(struct usb_serial_port *port) +static int aircable_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); return serial_buf_data_avail(priv->tx_buf); } -static int aircable_write(struct usb_serial_port *port, +static int aircable_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *source, int count) { struct aircable_private *priv = usb_get_serial_port_data(port); @@ -466,7 +467,7 @@ static void aircable_read_bulk_callback(struct urb *urb) if (status) { dbg("%s - urb status = %d", __func__, status); - if (!port->open_count) { + if (!port->port.count) { dbg("%s - port is closed, exiting.", __func__); return; } @@ -494,7 +495,7 @@ static void aircable_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, urb->transfer_buffer); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { if (urb->actual_length <= 2) { /* This is an incomplete package */ @@ -528,7 +529,7 @@ static void aircable_read_bulk_callback(struct urb *urb) } /* Schedule the next read _if_ we are still open */ - if (port->open_count) { + if (port->port.count) { usb_fill_bulk_urb(port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), @@ -547,8 +548,9 @@ static void aircable_read_bulk_callback(struct urb *urb) } /* Based on ftdi_sio.c throttle */ -static void aircable_throttle(struct usb_serial_port *port) +static void aircable_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -560,8 +562,9 @@ static void aircable_throttle(struct usb_serial_port *port) } /* Based on ftdi_sio.c unthrottle */ -static void aircable_unthrottle(struct usb_serial_port *port) +static void aircable_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); int actually_throttled; unsigned long flags; diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c new file mode 100644 index 000000000000..b3f1d1e82468 --- /dev/null +++ b/drivers/usb/serial/airprime.c @@ -0,0 +1,355 @@ +/* + * AirPrime CDMA Wireless Serial USB driver + * + * Copyright (C) 2005-2006 Greg Kroah-Hartman + * + * 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 +#include +#include +#include +#include +#include +#include + +static struct usb_device_id id_table [] = { + { USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */ + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +#define URB_TRANSFER_BUFFER_SIZE 4096 +#define NUM_READ_URBS 4 +#define NUM_WRITE_URBS 4 +#define NUM_BULK_EPS 3 +#define MAX_BULK_EPS 6 + +/* if overridden by the user, then use their value for the size of the + * read and write urbs, and the number of endpoints */ +static int buffer_size = URB_TRANSFER_BUFFER_SIZE; +static int endpoints = NUM_BULK_EPS; +static int debug; +struct airprime_private { + spinlock_t lock; + int outstanding_urbs; + int throttled; + struct urb *read_urbp[NUM_READ_URBS]; + + /* Settings for the port */ + int rts_state; /* Handshaking pins (outputs) */ + int dtr_state; + int cts_state; /* Handshaking pins (inputs) */ + int dsr_state; + int dcd_state; + int ri_state; +}; + +static int airprime_send_setup(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + struct airprime_private *priv; + + dbg("%s", __func__); + + if (port->number != 0) + return 0; + + priv = usb_get_serial_port_data(port); + + if (port->port.tty) { + int val = 0; + if (priv->dtr_state) + val |= 0x01; + if (priv->rts_state) + val |= 0x02; + + return usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + 0x22, 0x21, val, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + } + + return 0; +} + +static void airprime_read_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + unsigned char *data = urb->transfer_buffer; + struct tty_struct *tty; + int result; + int status = urb->status; + + dbg("%s - port %d", __func__, port->number); + + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __func__, status); + return; + } + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); + + tty = port->port.tty; + if (tty && urb->actual_length) { + tty_insert_flip_string(tty, data, urb->actual_length); + tty_flip_buffer_push(tty); + } + + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + return; +} + +static void airprime_write_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct airprime_private *priv = usb_get_serial_port_data(port); + int status = urb->status; + unsigned long flags; + + dbg("%s - port %d", __func__, port->number); + + /* free up the transfer buffer, as usb_free_urb() does not do this */ + kfree(urb->transfer_buffer); + + if (status) + dbg("%s - nonzero write bulk status received: %d", + __func__, status); + spin_lock_irqsave(&priv->lock, flags); + --priv->outstanding_urbs; + spin_unlock_irqrestore(&priv->lock, flags); + + usb_serial_port_softint(port); +} + +static int airprime_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) +{ + struct airprime_private *priv = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + struct urb *urb; + char *buffer = NULL; + int i; + int result = 0; + + dbg("%s - port %d", __func__, port->number); + + /* initialize our private data structure if it isn't already created */ + if (!priv) { + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + result = -ENOMEM; + goto out; + } + spin_lock_init(&priv->lock); + usb_set_serial_port_data(port, priv); + } + + /* Set some sane defaults */ + priv->rts_state = 1; + priv->dtr_state = 1; + + for (i = 0; i < NUM_READ_URBS; ++i) { + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!buffer) { + dev_err(&port->dev, "%s - out of memory.\n", + __func__); + result = -ENOMEM; + goto errout; + } + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + kfree(buffer); + dev_err(&port->dev, "%s - no more urbs?\n", + __func__); + result = -ENOMEM; + goto errout; + } + usb_fill_bulk_urb(urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + buffer, buffer_size, + airprime_read_bulk_callback, port); + result = usb_submit_urb(urb, GFP_KERNEL); + if (result) { + usb_free_urb(urb); + kfree(buffer); + dev_err(&port->dev, + "%s - failed submitting read urb %d for port %d, error %d\n", + __func__, i, port->number, result); + goto errout; + } + /* remember this urb so we can kill it when the + port is closed */ + priv->read_urbp[i] = urb; + } + + airprime_send_setup(port); + + goto out; + + errout: + /* some error happened, cancel any submitted urbs and clean up + anything that got allocated successfully */ + + while (i-- != 0) { + urb = priv->read_urbp[i]; + buffer = urb->transfer_buffer; + usb_kill_urb(urb); + usb_free_urb(urb); + kfree(buffer); + } + + out: + return result; +} + +static void airprime_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) +{ + struct airprime_private *priv = usb_get_serial_port_data(port); + int i; + + dbg("%s - port %d", __func__, port->number); + + priv->rts_state = 0; + priv->dtr_state = 0; + + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) + airprime_send_setup(port); + mutex_unlock(&port->serial->disc_mutex); + + for (i = 0; i < NUM_READ_URBS; ++i) { + usb_kill_urb(priv->read_urbp[i]); + kfree(priv->read_urbp[i]->transfer_buffer); + usb_free_urb(priv->read_urbp[i]); + } + + /* free up private structure */ + kfree(priv); + usb_set_serial_port_data(port, NULL); +} + +static int airprime_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) +{ + struct airprime_private *priv = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + struct urb *urb; + unsigned char *buffer; + unsigned long flags; + int status; + dbg("%s - port %d", __func__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + if (priv->outstanding_urbs > NUM_WRITE_URBS) { + spin_unlock_irqrestore(&priv->lock, flags); + dbg("%s - write limit hit\n", __func__); + return 0; + } + spin_unlock_irqrestore(&priv->lock, flags); + buffer = kmalloc(count, GFP_ATOMIC); + if (!buffer) { + dev_err(&port->dev, "out of memory\n"); + return -ENOMEM; + } + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + dev_err(&port->dev, "no more free urbs\n"); + kfree(buffer); + return -ENOMEM; + } + memcpy(buffer, buf, count); + + usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); + + usb_fill_bulk_urb(urb, serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + buffer, count, + airprime_write_bulk_callback, port); + + /* send it down the pipe */ + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + dev_err(&port->dev, + "%s - usb_submit_urb(write bulk) failed with status = %d\n", + __func__, status); + count = status; + kfree(buffer); + } else { + spin_lock_irqsave(&priv->lock, flags); + ++priv->outstanding_urbs; + spin_unlock_irqrestore(&priv->lock, flags); + } + /* we are done with this urb, so let the host driver + * really free it when it is finished with it */ + usb_free_urb(urb); + return count; +} + +static struct usb_driver airprime_driver = { + .name = "airprime", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, + .no_dynamic_id = 1, +}; + +static struct usb_serial_driver airprime_device = { + .driver = { + .owner = THIS_MODULE, + .name = "airprime", + }, + .usb_driver = &airprime_driver, + .id_table = id_table, + .open = airprime_open, + .close = airprime_close, + .write = airprime_write, +}; + +static int __init airprime_init(void) +{ + int retval; + + airprime_device.num_ports = endpoints; + if (endpoints < 0 || endpoints >= MAX_BULK_EPS) + airprime_device.num_ports = NUM_BULK_EPS; + + retval = usb_serial_register(&airprime_device); + if (retval) + return retval; + retval = usb_register(&airprime_driver); + if (retval) + usb_serial_deregister(&airprime_device); + return retval; +} + +static void __exit airprime_exit(void) +{ + dbg("%s", __func__); + + usb_deregister(&airprime_driver); + usb_serial_deregister(&airprime_device); +} + +module_init(airprime_init); +module_exit(airprime_exit); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled"); +module_param(buffer_size, int, 0); +MODULE_PARM_DESC(buffer_size, + "Size of the transfer buffers in bytes (default 4096)"); +module_param(endpoints, int, 0); +MODULE_PARM_DESC(endpoints, "Number of bulk EPs to configure (default 3)"); diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 77895c8f8f31..aec61880f36c 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -158,12 +158,13 @@ cleanup: return -ENOMEM; } -static void ark3116_set_termios(struct usb_serial_port *port, +static void ark3116_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct ark3116_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; unsigned long flags; int baud; @@ -177,8 +178,8 @@ static void ark3116_set_termios(struct usb_serial_port *port, spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B9600 | CS8 + *termios = tty_std_termios; + termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; termios->c_ispeed = 9600; termios->c_ospeed = 9600; @@ -192,7 +193,7 @@ static void ark3116_set_termios(struct usb_serial_port *port, buf = kmalloc(1, GFP_KERNEL); if (!buf) { dbg("error kmalloc"); - *port->tty->termios = *old_termios; + *termios = *old_termios; return; } @@ -243,7 +244,7 @@ static void ark3116_set_termios(struct usb_serial_port *port, } /* set baudrate */ - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); switch (baud) { case 75: @@ -262,11 +263,11 @@ static void ark3116_set_termios(struct usb_serial_port *port, case 230400: case 460800: /* Report the resulting rate back to the caller */ - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); break; /* set 9600 as default (if given baudrate is invalid for example) */ default: - tty_encode_baud_rate(port->tty, 9600, 9600); + tty_encode_baud_rate(tty, 9600, 9600); case 0: baud = 9600; } @@ -317,7 +318,8 @@ static void ark3116_set_termios(struct usb_serial_port *port, return; } -static int ark3116_open(struct usb_serial_port *port, struct file *filp) +static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -332,7 +334,7 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp) return -ENOMEM; } - result = usb_serial_generic_open(port, filp); + result = usb_serial_generic_open(tty, port, filp); if (result) goto err_out; @@ -362,8 +364,8 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp) ARK3116_RCV(serial, 124, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf); /* initialise termios */ - if (port->tty) - ark3116_set_termios(port, &tmp_termios); + if (tty) + ark3116_set_termios(tty, port, &tmp_termios); err_out: kfree(buf); @@ -371,9 +373,10 @@ err_out: return result; } -static int ark3116_ioctl(struct usb_serial_port *port, struct file *file, +static int ark3116_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; @@ -403,8 +406,9 @@ static int ark3116_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file) +static int ark3116_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; char *buf; char temp; diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 0a322fc53d6e..1a762692c739 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -89,14 +89,13 @@ static int debug; /* function prototypes for a Belkin USB Serial Adapter F5U103 */ static int belkin_sa_startup (struct usb_serial *serial); static void belkin_sa_shutdown (struct usb_serial *serial); -static int belkin_sa_open (struct usb_serial_port *port, struct file *filp); -static void belkin_sa_close (struct usb_serial_port *port, struct file *filp); +static int belkin_sa_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void belkin_sa_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void belkin_sa_read_int_callback (struct urb *urb); -static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int belkin_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void belkin_sa_break_ctl (struct usb_serial_port *port, int break_state ); -static int belkin_sa_tiocmget (struct usb_serial_port *port, struct file *file); -static int belkin_sa_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); +static void belkin_sa_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); +static void belkin_sa_break_ctl (struct tty_struct *tty, int break_state ); +static int belkin_sa_tiocmget (struct tty_struct *tty, struct file *file); +static int belkin_sa_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static struct usb_device_id id_table_combined [] = { @@ -132,7 +131,6 @@ static struct usb_serial_driver belkin_device = { .open = belkin_sa_open, .close = belkin_sa_close, .read_int_callback = belkin_sa_read_int_callback, /* How we get the status info */ - .ioctl = belkin_sa_ioctl, .set_termios = belkin_sa_set_termios, .break_ctl = belkin_sa_break_ctl, .tiocmget = belkin_sa_tiocmget, @@ -190,7 +188,7 @@ static int belkin_sa_startup (struct usb_serial *serial) } -static void belkin_sa_shutdown (struct usb_serial *serial) +static void belkin_sa_shutdown(struct usb_serial *serial) { struct belkin_sa_private *priv; int i; @@ -206,7 +204,7 @@ static void belkin_sa_shutdown (struct usb_serial *serial) } -static int belkin_sa_open (struct usb_serial_port *port, struct file *filp) +static int belkin_sa_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) { int retval = 0; @@ -235,7 +233,8 @@ exit: } /* belkin_sa_open */ -static void belkin_sa_close (struct usb_serial_port *port, struct file *filp) +static void belkin_sa_close (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s port %d", __func__, port->number); @@ -246,7 +245,7 @@ static void belkin_sa_close (struct usb_serial_port *port, struct file *filp) } /* belkin_sa_close */ -static void belkin_sa_read_int_callback (struct urb *urb) +static void belkin_sa_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct belkin_sa_private *priv; @@ -311,7 +310,7 @@ static void belkin_sa_read_int_callback (struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & BELKIN_SA_LSR_ERR) { - tty = port->tty; + tty = port->port.tty; /* Overrun Error */ if (priv->last_lsr & BELKIN_SA_LSR_OE) { } @@ -334,7 +333,8 @@ exit: __func__, retval); } -static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void belkin_sa_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct belkin_sa_private *priv = usb_get_serial_port_data(port); @@ -347,7 +347,7 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios unsigned long control_state; int bad_flow_control; speed_t baud; - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; iflag = termios->c_iflag; cflag = termios->c_cflag; @@ -377,7 +377,7 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios } } - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); if (baud) { urb_value = BELKIN_SA_BAUD(baud); /* Clip to maximum speed */ @@ -387,7 +387,7 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios baud = BELKIN_SA_BAUD(urb_value); /* Report the actual baud rate back to the caller */ - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0) err("Set baudrate error"); } else { @@ -463,8 +463,9 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios } /* belkin_sa_set_termios */ -static void belkin_sa_break_ctl( struct usb_serial_port *port, int break_state ) +static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0) @@ -472,8 +473,9 @@ static void belkin_sa_break_ctl( struct usb_serial_port *port, int break_state ) } -static int belkin_sa_tiocmget (struct usb_serial_port *port, struct file *file) +static int belkin_sa_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct belkin_sa_private *priv = usb_get_serial_port_data(port); unsigned long control_state; unsigned long flags; @@ -488,9 +490,10 @@ static int belkin_sa_tiocmget (struct usb_serial_port *port, struct file *file) } -static int belkin_sa_tiocmset (struct usb_serial_port *port, struct file *file, +static int belkin_sa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct belkin_sa_private *priv = usb_get_serial_port_data(port); unsigned long control_state; @@ -540,29 +543,7 @@ exit: } -static int belkin_sa_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - return( 0 ); - - case TIOCGICOUNT: - /* return count of modemline transitions */ - /* TODO */ - return 0; - - default: - dbg("belkin_sa_ioctl arg not supported - 0x%04x",cmd); - return(-ENOIOCTLCMD); - break; - } - return 0; -} /* belkin_sa_ioctl */ - - -static int __init belkin_sa_init (void) +static int __init belkin_sa_init(void) { int retval; retval = usb_serial_register(&belkin_device); @@ -583,7 +564,7 @@ failed_usb_serial_register: static void __exit belkin_sa_exit (void) { usb_deregister (&belkin_driver); - usb_serial_deregister (&belkin_device); + usb_serial_deregister(&belkin_device); } diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 1f7c86bd8297..f61e3ca64305 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -232,7 +232,8 @@ error: kfree(priv); } /* open this device, set default parameters */ -static int ch341_open(struct usb_serial_port *port, struct file *filp) +static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct usb_serial *serial = port->serial; struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]); @@ -256,7 +257,7 @@ static int ch341_open(struct usb_serial_port *port, struct file *filp) if (r) goto out; - r = usb_serial_generic_open(port, filp); + r = usb_serial_generic_open(tty, port, filp); out: return r; } @@ -264,11 +265,10 @@ out: return r; /* Old_termios contains the original termios settings and * tty->termios contains the new setting to be used. */ -static void ch341_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void ch341_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct ch341_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned baud_rate; dbg("ch341_set_termios()"); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 201184c3fb87..940f5de68980 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -145,12 +145,12 @@ static int usb_console_setup(struct console *co, char *options) } port = serial->port[0]; - port->tty = NULL; + port->port.tty = NULL; info->port = port; - ++port->open_count; - if (port->open_count == 1) { + ++port->port.count; + if (port->port.count == 1) { if (serial->type->set_termios) { /* * allocate a fake tty so the driver can initialize @@ -171,15 +171,15 @@ static int usb_console_setup(struct console *co, char *options) } memset(&dummy, 0, sizeof(struct ktermios)); tty->termios = termios; - port->tty = tty; + port->port.tty = tty; } /* only call the device specific open if this * is the first time the port is opened */ if (serial->type->open) - retval = serial->type->open(port, NULL); + retval = serial->type->open(NULL, port, NULL); else - retval = usb_serial_generic_open(port, NULL); + retval = usb_serial_generic_open(NULL, port, NULL); if (retval) { err("could not open USB console port"); @@ -188,9 +188,9 @@ static int usb_console_setup(struct console *co, char *options) if (serial->type->set_termios) { termios->c_cflag = cflag; - serial->type->set_termios(port, &dummy); + serial->type->set_termios(NULL, port, &dummy); - port->tty = NULL; + port->port.tty = NULL; kfree(termios); kfree(tty); } @@ -203,11 +203,11 @@ out: return retval; free_termios: kfree(termios); - port->tty = NULL; + port->port.tty = NULL; free_tty: kfree(tty); reset_open_count: - port->open_count = 0; + port->port.count = 0; goto out; } @@ -227,7 +227,7 @@ static void usb_console_write(struct console *co, const char *buf, unsigned coun dbg("%s - port %d, %d byte(s)", __func__, port->number, count); - if (!port->open_count) { + if (!port->port.count) { dbg ("%s - port not opened", __func__); return; } @@ -245,17 +245,17 @@ static void usb_console_write(struct console *co, const char *buf, unsigned coun } /* pass on to the driver specific version of this function if it is available */ if (serial->type->write) - retval = serial->type->write(port, buf, i); + retval = serial->type->write(NULL, port, buf, i); else - retval = usb_serial_generic_write(port, buf, i); + retval = usb_serial_generic_write(NULL, port, buf, i); dbg("%s - return value : %d", __func__, retval); if (lf) { /* append CR after LF */ unsigned char cr = 13; if (serial->type->write) - retval = serial->type->write(port, &cr, 1); + retval = serial->type->write(NULL, port, &cr, 1); else - retval = usb_serial_generic_write(port, &cr, 1); + retval = usb_serial_generic_write(NULL, port, &cr, 1); dbg("%s - return value : %d", __func__, retval); } buf += i; @@ -306,8 +306,8 @@ void usb_serial_console_exit (void) { if (usbcons_info.port) { unregister_console(&usbcons); - if (usbcons_info.port->open_count) - usbcons_info.port->open_count--; + if (usbcons_info.port->port.count) + usbcons_info.port->port.count--; usbcons_info.port = NULL; } } diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 2bc5576c443a..46c33fc9f6ce 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -37,15 +37,18 @@ /* * Function Prototypes */ -static int cp2101_open(struct usb_serial_port*, struct file*); -static void cp2101_cleanup(struct usb_serial_port*); -static void cp2101_close(struct usb_serial_port*, struct file*); -static void cp2101_get_termios(struct usb_serial_port*); -static void cp2101_set_termios(struct usb_serial_port*, struct ktermios*); -static int cp2101_tiocmget (struct usb_serial_port *, struct file *); -static int cp2101_tiocmset (struct usb_serial_port *, struct file *, +static int cp2101_open(struct tty_struct *, struct usb_serial_port *, + struct file *); +static void cp2101_cleanup(struct usb_serial_port *); +static void cp2101_close(struct tty_struct *, struct usb_serial_port *, + struct file*); +static void cp2101_get_termios(struct tty_struct *); +static void cp2101_set_termios(struct tty_struct *, struct usb_serial_port *, + struct ktermios*); +static int cp2101_tiocmget (struct tty_struct *, struct file *); +static int cp2101_tiocmset (struct tty_struct *, struct file *, unsigned int, unsigned int); -static void cp2101_break_ctl(struct usb_serial_port*, int); +static void cp2101_break_ctl(struct tty_struct *, int); static int cp2101_startup (struct usb_serial *); static void cp2101_shutdown(struct usb_serial*); @@ -182,7 +185,7 @@ static struct usb_serial_driver cp2101_device = { * 'data' is a pointer to a pre-allocated array of integers large * enough to hold 'size' bytes (with 4 bytes to each integer) */ -static int cp2101_get_config(struct usb_serial_port* port, u8 request, +static int cp2101_get_config(struct usb_serial_port *port, u8 request, unsigned int *data, int size) { struct usb_serial *serial = port->serial; @@ -228,7 +231,7 @@ static int cp2101_get_config(struct usb_serial_port* port, u8 request, * Values less than 16 bits wide are sent directly * 'size' is specified in bytes. */ -static int cp2101_set_config(struct usb_serial_port* port, u8 request, +static int cp2101_set_config(struct usb_serial_port *port, u8 request, unsigned int *data, int size) { struct usb_serial *serial = port->serial; @@ -283,13 +286,14 @@ static int cp2101_set_config(struct usb_serial_port* port, u8 request, * Convenience function for calling cp2101_set_config on single data values * without requiring an integer pointer */ -static inline int cp2101_set_config_single(struct usb_serial_port* port, +static inline int cp2101_set_config_single(struct usb_serial_port *port, u8 request, unsigned int data) { return cp2101_set_config(port, request, &data, 2); } -static int cp2101_open (struct usb_serial_port *port, struct file *filp) +static int cp2101_open (struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct usb_serial *serial = port->serial; int result; @@ -318,10 +322,10 @@ static int cp2101_open (struct usb_serial_port *port, struct file *filp) } /* Configure the termios structure */ - cp2101_get_termios(port); + cp2101_get_termios(tty); /* Set the DTR and RTS pins low */ - cp2101_tiocmset(port, NULL, TIOCM_DTR | TIOCM_RTS, 0); + cp2101_tiocmset(tty, NULL, TIOCM_DTR | TIOCM_RTS, 0); return 0; } @@ -341,7 +345,8 @@ static void cp2101_cleanup (struct usb_serial_port *port) } } -static void cp2101_close (struct usb_serial_port *port, struct file * filp) +static void cp2101_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file * filp) { dbg("%s - port %d", __func__, port->number); @@ -362,19 +367,15 @@ static void cp2101_close (struct usb_serial_port *port, struct file * filp) * from the device, corrects any unsupported values, and configures the * termios structure to reflect the state of the device */ -static void cp2101_get_termios (struct usb_serial_port *port) +static void cp2101_get_termios (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned int cflag, modem_ctl[4]; unsigned int baud; unsigned int bits; dbg("%s - port %d", __func__, port->number); - if (!port->tty || !port->tty->termios) { - dbg("%s - no tty structures", __func__); - return; - } - cp2101_get_config(port, CP2101_BAUDRATE, &baud, 2); /* Convert to baudrate */ if (baud) @@ -382,8 +383,8 @@ static void cp2101_get_termios (struct usb_serial_port *port) dbg("%s - baud rate = %d", __func__, baud); - tty_encode_baud_rate(port->tty, baud, baud); - cflag = port->tty->termios->c_cflag; + tty_encode_baud_rate(tty, baud, baud); + cflag = tty->termios->c_cflag; cp2101_get_config(port, CP2101_BITS, &bits, 2); cflag &= ~CSIZE; @@ -491,11 +492,11 @@ static void cp2101_get_termios (struct usb_serial_port *port) cflag &= ~CRTSCTS; } - port->tty->termios->c_cflag = cflag; + tty->termios->c_cflag = cflag; } -static void cp2101_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void cp2101_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { unsigned int cflag, old_cflag; unsigned int baud = 0, bits; @@ -503,15 +504,13 @@ static void cp2101_set_termios (struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); - if (!port->tty || !port->tty->termios) { - dbg("%s - no tty structures", __func__); + if (!tty) return; - } - port->tty->termios->c_cflag &= ~CMSPAR; - cflag = port->tty->termios->c_cflag; + tty->termios->c_cflag &= ~CMSPAR; + cflag = tty->termios->c_cflag; old_cflag = old_termios->c_cflag; - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); /* If the baud rate is to be updated*/ if (baud != tty_termios_baud_rate(old_termios)) { @@ -554,7 +553,7 @@ static void cp2101_set_termios (struct usb_serial_port *port, } } /* Report back the resulting baud rate */ - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); /* If the number of data bits is to be updated */ if ((cflag & CSIZE) != (old_cflag & CSIZE)) { @@ -651,9 +650,10 @@ static void cp2101_set_termios (struct usb_serial_port *port, } -static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file, +static int cp2101_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; unsigned int control = 0; dbg("%s - port %d", __func__, port->number); @@ -681,8 +681,9 @@ static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file, } -static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file) +static int cp2101_tiocmget (struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; unsigned int control; int result; @@ -702,8 +703,9 @@ static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file) return result; } -static void cp2101_break_ctl (struct usb_serial_port *port, int break_state) +static void cp2101_break_ctl (struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; unsigned int state; dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index c164e2cf2752..546178ea6f2d 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -57,15 +57,18 @@ static int debug; #define CYBERJACK_PRODUCT_ID 0x0100 /* Function prototypes */ -static int cyberjack_startup (struct usb_serial *serial); -static void cyberjack_shutdown (struct usb_serial *serial); -static int cyberjack_open (struct usb_serial_port *port, struct file *filp); -static void cyberjack_close (struct usb_serial_port *port, struct file *filp); -static int cyberjack_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int cyberjack_write_room( struct usb_serial_port *port ); -static void cyberjack_read_int_callback (struct urb *urb); -static void cyberjack_read_bulk_callback (struct urb *urb); -static void cyberjack_write_bulk_callback (struct urb *urb); +static int cyberjack_startup(struct usb_serial *serial); +static void cyberjack_shutdown(struct usb_serial *serial); +static int cyberjack_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void cyberjack_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static int cyberjack_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); +static int cyberjack_write_room( struct tty_struct *tty); +static void cyberjack_read_int_callback(struct urb *urb); +static void cyberjack_read_bulk_callback(struct urb *urb); +static void cyberjack_write_bulk_callback(struct urb *urb); static struct usb_device_id id_table [] = { { USB_DEVICE(CYBERJACK_VENDOR_ID, CYBERJACK_PRODUCT_ID) }, @@ -111,7 +114,7 @@ struct cyberjack_private { }; /* do some startup allocations not currently performed by usb_serial_probe() */ -static int cyberjack_startup (struct usb_serial *serial) +static int cyberjack_startup(struct usb_serial *serial) { struct cyberjack_private *priv; int i; @@ -145,7 +148,7 @@ static int cyberjack_startup (struct usb_serial *serial) return( 0 ); } -static void cyberjack_shutdown (struct usb_serial *serial) +static void cyberjack_shutdown(struct usb_serial *serial) { int i; @@ -159,7 +162,8 @@ static void cyberjack_shutdown (struct usb_serial *serial) } } -static int cyberjack_open (struct usb_serial_port *port, struct file *filp) +static int cyberjack_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct cyberjack_private *priv; unsigned long flags; @@ -174,7 +178,8 @@ static int cyberjack_open (struct usb_serial_port *port, struct file *filp) * the data through, otherwise it is scheduled, and with high * data rates (like with OHCI) data can get lost. */ - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; priv = usb_get_serial_port_data(port); spin_lock_irqsave(&priv->lock, flags); @@ -186,7 +191,8 @@ static int cyberjack_open (struct usb_serial_port *port, struct file *filp) return result; } -static void cyberjack_close (struct usb_serial_port *port, struct file *filp) +static void cyberjack_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); @@ -197,7 +203,8 @@ static void cyberjack_close (struct usb_serial_port *port, struct file *filp) } } -static int cyberjack_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int cyberjack_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct cyberjack_private *priv = usb_get_serial_port_data(port); @@ -292,13 +299,13 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b return (count); } -static int cyberjack_write_room( struct usb_serial_port *port ) +static int cyberjack_write_room(struct tty_struct *tty) { /* FIXME: .... */ return CYBERJACK_LOCAL_BUF_SIZE; } -static void cyberjack_read_int_callback( struct urb *urb ) +static void cyberjack_read_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); @@ -355,7 +362,7 @@ resubmit: dbg("%s - usb_submit_urb(int urb)", __func__); } -static void cyberjack_read_bulk_callback (struct urb *urb) +static void cyberjack_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); @@ -374,7 +381,7 @@ static void cyberjack_read_bulk_callback (struct urb *urb) return; } - tty = port->tty; + tty = port->port.tty; if (!tty) { dbg("%s - ignoring since device not open\n", __func__); return; @@ -407,7 +414,7 @@ static void cyberjack_read_bulk_callback (struct urb *urb) } } -static void cyberjack_write_bulk_callback (struct urb *urb) +static void cyberjack_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 0230d3c0888a..6999d3372d85 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -167,18 +167,18 @@ static int cypress_earthmate_startup (struct usb_serial *serial); static int cypress_hidcom_startup (struct usb_serial *serial); static int cypress_ca42v2_startup (struct usb_serial *serial); static void cypress_shutdown (struct usb_serial *serial); -static int cypress_open (struct usb_serial_port *port, struct file *filp); -static void cypress_close (struct usb_serial_port *port, struct file *filp); -static int cypress_write (struct usb_serial_port *port, const unsigned char *buf, int count); +static int cypress_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void cypress_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int cypress_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static void cypress_send (struct usb_serial_port *port); -static int cypress_write_room (struct usb_serial_port *port); -static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void cypress_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int cypress_tiocmget (struct usb_serial_port *port, struct file *file); -static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); -static int cypress_chars_in_buffer (struct usb_serial_port *port); -static void cypress_throttle (struct usb_serial_port *port); -static void cypress_unthrottle (struct usb_serial_port *port); +static int cypress_write_room (struct tty_struct *tty); +static int cypress_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); +static void cypress_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); +static int cypress_tiocmget (struct tty_struct *tty, struct file *file); +static int cypress_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); +static int cypress_chars_in_buffer (struct tty_struct *tty); +static void cypress_throttle (struct tty_struct *tty); +static void cypress_unthrottle (struct tty_struct *tty); static void cypress_set_dead (struct usb_serial_port *port); static void cypress_read_int_callback (struct urb *urb); static void cypress_write_int_callback (struct urb *urb); @@ -322,8 +322,10 @@ static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate) /* This function can either set or retrieve the current serial line settings */ -static int cypress_serial_control (struct usb_serial_port *port, speed_t baud_rate, int data_bits, int stop_bits, - int parity_enable, int parity_type, int reset, int cypress_request_type) +static int cypress_serial_control (struct tty_struct *tty, + struct usb_serial_port *port, speed_t baud_rate, int data_bits, + int stop_bits, int parity_enable, int parity_type, int reset, + int cypress_request_type) { int new_baudrate = 0, retval = 0, tries = 0; struct cypress_private *priv; @@ -395,7 +397,7 @@ static int cypress_serial_control (struct usb_serial_port *port, speed_t baud_ra spin_unlock_irqrestore(&priv->lock, flags); /* If we asked for a speed change encode it */ if (baud_rate) - tty_encode_baud_rate(port->tty, + tty_encode_baud_rate(tty, new_baudrate, new_baudrate); } break; @@ -611,7 +613,8 @@ static void cypress_shutdown (struct usb_serial *serial) } -static int cypress_open (struct usb_serial_port *port, struct file *filp) +static int cypress_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct cypress_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -636,14 +639,15 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) spin_unlock_irqrestore(&priv->lock, flags); /* setting to zero could cause data loss */ - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* raise both lines and set termios */ spin_lock_irqsave(&priv->lock, flags); priv->line_control = CONTROL_DTR | CONTROL_RTS; priv->cmd_ctrl = 1; spin_unlock_irqrestore(&priv->lock, flags); - result = cypress_write(port, NULL, 0); + result = cypress_write(tty, port, NULL, 0); if (result) { dev_err(&port->dev, "%s - failed setting the control lines - error %d\n", __func__, result); @@ -651,7 +655,8 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) } else dbg("%s - success setting the control lines", __func__); - cypress_set_termios(port, &priv->tmp_termios); + if (tty) + cypress_set_termios(tty, port, &priv->tmp_termios); /* setup the port and start reading from the device */ if(!port->interrupt_in_urb){ @@ -674,7 +679,8 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp) } /* cypress_open */ -static void cypress_close(struct usb_serial_port *port, struct file * filp) +static void cypress_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct cypress_private *priv = usb_get_serial_port_data(port); unsigned int c_cflag; @@ -688,7 +694,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) spin_lock_irq(&priv->lock); timeout = CYPRESS_CLOSING_WAIT; init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (cypress_buf_data_avail(priv->buf) == 0 @@ -701,7 +707,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) spin_lock_irq(&priv->lock); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); /* clear out any remaining data in the buffer */ cypress_buf_clear(priv->buf); spin_unlock_irq(&priv->lock); @@ -713,19 +719,21 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) return; } /* wait for characters to drain from device */ - bps = tty_get_baud_rate(port->tty); - if (bps > 1200) - timeout = max((HZ*2560)/bps,HZ/10); - else - timeout = 2*HZ; - schedule_timeout_interruptible(timeout); + if (tty) { + bps = tty_get_baud_rate(tty); + if (bps > 1200) + timeout = max((HZ*2560)/bps,HZ/10); + else + timeout = 2*HZ; + schedule_timeout_interruptible(timeout); + } dbg("%s - stopping urbs", __func__); usb_kill_urb (port->interrupt_in_urb); usb_kill_urb (port->interrupt_out_urb); - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop dtr and rts */ priv = usb_get_serial_port_data(port); @@ -733,7 +741,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) priv->line_control = 0; priv->cmd_ctrl = 1; spin_unlock_irq(&priv->lock); - cypress_write(port, NULL, 0); + cypress_write(tty, port, NULL, 0); } } @@ -744,7 +752,8 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) } /* cypress_close */ -static int cypress_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -878,8 +887,9 @@ send: /* returns how much space is available in the soft buffer */ -static int cypress_write_room(struct usb_serial_port *port) +static int cypress_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -895,8 +905,9 @@ static int cypress_write_room(struct usb_serial_port *port) } -static int cypress_tiocmget (struct usb_serial_port *port, struct file *file) +static int cypress_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); __u8 status, control; unsigned int result = 0; @@ -922,9 +933,10 @@ static int cypress_tiocmget (struct usb_serial_port *port, struct file *file) } -static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, +static int cypress_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -942,12 +954,14 @@ static int cypress_tiocmset (struct usb_serial_port *port, struct file *file, priv->cmd_ctrl = 1; spin_unlock_irqrestore(&priv->lock, flags); - return cypress_write(port, NULL, 0); + return cypress_write(tty, port, NULL, 0); } -static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int cypress_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); @@ -983,22 +997,18 @@ static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsi } } return 0; - break; default: break; } - dbg("%s - arg not supported - it was 0x%04x - check include/asm/ioctls.h", __func__, cmd); - return -ENOIOCTLCMD; } /* cypress_ioctl */ -static void cypress_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void cypress_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct cypress_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty; int data_bits, stop_bits, parity_type, parity_enable; unsigned cflag, iflag; unsigned long flags; @@ -1007,8 +1017,6 @@ static void cypress_set_termios (struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); - tty = port->tty; - spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { if (priv->chiptype == CT_EARTHMATE) { @@ -1096,13 +1104,13 @@ static void cypress_set_termios (struct usb_serial_port *port, "%d data_bits (+5)", __func__, stop_bits, parity_enable, parity_type, data_bits); - cypress_serial_control(port, tty_get_baud_rate(tty), data_bits, stop_bits, + cypress_serial_control(tty, port, tty_get_baud_rate(tty), data_bits, stop_bits, parity_enable, parity_type, 0, CYPRESS_SET_CONFIG); /* we perform a CYPRESS_GET_CONFIG so that the current settings are * filled into the private structure this should confirm that all is * working if it returns what we just set */ - cypress_serial_control(port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG); + cypress_serial_control(tty, port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG); /* Here we can define custom tty settings for devices; the main tty * termios flag base comes from empeg.c */ @@ -1142,14 +1150,15 @@ static void cypress_set_termios (struct usb_serial_port *port, /* if necessary, set lines */ if (linechange) { priv->cmd_ctrl = 1; - cypress_write(port, NULL, 0); + cypress_write(tty, port, NULL, 0); } } /* cypress_set_termios */ /* returns amount of data still left in soft buffer */ -static int cypress_chars_in_buffer(struct usb_serial_port *port) +static int cypress_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -1165,8 +1174,9 @@ static int cypress_chars_in_buffer(struct usb_serial_port *port) } -static void cypress_throttle (struct usb_serial_port *port) +static void cypress_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -1178,8 +1188,9 @@ static void cypress_throttle (struct usb_serial_port *port) } -static void cypress_unthrottle (struct usb_serial_port *port) +static void cypress_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int actually_throttled, result; unsigned long flags; @@ -1251,7 +1262,7 @@ static void cypress_read_int_callback(struct urb *urb) } spin_unlock_irqrestore(&priv->lock, flags); - tty = port->tty; + tty = port->port.tty; if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1327,7 +1338,7 @@ static void cypress_read_int_callback(struct urb *urb) data[i]); tty_insert_flip_char(tty, data[i], tty_flag); } - tty_flip_buffer_push(port->tty); + tty_flip_buffer_push(port->port.tty); } spin_lock_irqsave(&priv->lock, flags); @@ -1339,7 +1350,7 @@ continue_read: /* Continue trying to always read... unless the port has closed. */ - if (port->open_count > 0 && priv->comm_is_ok) { + if (port->port.count > 0 && priv->comm_is_ok) { usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress), diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 28bc6fcf44f0..dc92dbfc962e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -15,7 +15,7 @@ * Al Borchers (borchers@steinerpoint.com) * * (12/03/2001) gkh -* switched to using port->open_count instead of private version. +* switched to using port->port.count instead of private version. * Removed port->active * * (04/08/2001) gb @@ -441,22 +441,23 @@ static int digi_set_modem_signals(struct usb_serial_port *port, unsigned int modem_signals, int interruptible); static int digi_transmit_idle(struct usb_serial_port *port, unsigned long timeout); -static void digi_rx_throttle (struct usb_serial_port *port); -static void digi_rx_unthrottle (struct usb_serial_port *port); -static void digi_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios); -static void digi_break_ctl(struct usb_serial_port *port, int break_state); -static int digi_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg); -static int digi_tiocmget(struct usb_serial_port *port, struct file *file); -static int digi_tiocmset(struct usb_serial_port *port, struct file *file, +static void digi_rx_throttle (struct tty_struct *tty); +static void digi_rx_unthrottle (struct tty_struct *tty); +static void digi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); +static void digi_break_ctl(struct tty_struct *tty, int break_state); +static int digi_tiocmget(struct tty_struct *tty, struct file *file); +static int digi_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static int digi_write(struct usb_serial_port *port, const unsigned char *buf, int count); +static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); static void digi_write_bulk_callback(struct urb *urb); -static int digi_write_room(struct usb_serial_port *port); -static int digi_chars_in_buffer(struct usb_serial_port *port); -static int digi_open(struct usb_serial_port *port, struct file *filp); -static void digi_close(struct usb_serial_port *port, struct file *filp); +static int digi_write_room(struct tty_struct *tty); +static int digi_chars_in_buffer(struct tty_struct *tty); +static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static void digi_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); static int digi_startup_device(struct usb_serial *serial); static int digi_startup(struct usb_serial *serial); static void digi_shutdown(struct usb_serial *serial); @@ -516,7 +517,6 @@ static struct usb_serial_driver digi_acceleport_2_device = { .chars_in_buffer = digi_chars_in_buffer, .throttle = digi_rx_throttle, .unthrottle = digi_rx_unthrottle, - .ioctl = digi_ioctl, .set_termios = digi_set_termios, .break_ctl = digi_break_ctl, .tiocmget = digi_tiocmget, @@ -543,7 +543,6 @@ static struct usb_serial_driver digi_acceleport_4_device = { .chars_in_buffer = digi_chars_in_buffer, .throttle = digi_rx_throttle, .unthrottle = digi_rx_unthrottle, - .ioctl = digi_ioctl, .set_termios = digi_set_termios, .break_ctl = digi_break_ctl, .tiocmget = digi_tiocmget, @@ -604,7 +603,7 @@ static void digi_wakeup_write_lock(struct work_struct *work) static void digi_wakeup_write(struct usb_serial_port *port) { - tty_wakeup(port->tty); + tty_wakeup(port->port.tty); } @@ -856,9 +855,10 @@ static int digi_transmit_idle(struct usb_serial_port *port, } -static void digi_rx_throttle(struct usb_serial_port *port) +static void digi_rx_throttle(struct tty_struct *tty) { unsigned long flags; + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); @@ -872,10 +872,11 @@ static void digi_rx_throttle(struct usb_serial_port *port) } -static void digi_rx_unthrottle(struct usb_serial_port *port) +static void digi_rx_unthrottle(struct tty_struct *tty) { int ret = 0; unsigned long flags; + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); dbg("digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num); @@ -900,12 +901,10 @@ static void digi_rx_unthrottle(struct usb_serial_port *port) } -static void digi_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void digi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - struct digi_port *priv = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned int iflag = tty->termios->c_iflag; unsigned int cflag = tty->termios->c_cflag; unsigned int old_iflag = old_termios->c_iflag; @@ -1088,8 +1087,9 @@ static void digi_set_termios(struct usb_serial_port *port, } -static void digi_break_ctl(struct usb_serial_port *port, int break_state) +static void digi_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; unsigned char buf[4]; buf[0] = DIGI_CMD_BREAK_CONTROL; @@ -1100,8 +1100,9 @@ static void digi_break_ctl(struct usb_serial_port *port, int break_state) } -static int digi_tiocmget(struct usb_serial_port *port, struct file *file) +static int digi_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); unsigned int val; unsigned long flags; @@ -1115,9 +1116,10 @@ static int digi_tiocmget(struct usb_serial_port *port, struct file *file) } -static int digi_tiocmset(struct usb_serial_port *port, struct file *file, +static int digi_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); unsigned int val; unsigned long flags; @@ -1131,27 +1133,8 @@ static int digi_tiocmset(struct usb_serial_port *port, struct file *file, } -static int digi_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct digi_port *priv = usb_get_serial_port_data(port); - dbg("digi_ioctl: TOP: port=%d, cmd=0x%x", priv->dp_port_num, cmd); - - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - return 0; - case TIOCGICOUNT: - /* return count of modemline transitions */ - /* TODO */ - return 0; - } - return -ENOIOCTLCMD; - -} - -static int digi_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { int ret,data_len,new_len; @@ -1261,7 +1244,7 @@ static void digi_write_bulk_callback(struct urb *urb) /* try to send any buffered data on this port, if it is open */ spin_lock(&priv->dp_port_lock); priv->dp_write_urb_in_use = 0; - if (port->open_count && port->write_urb->status != -EINPROGRESS + if (port->port.count && port->write_urb->status != -EINPROGRESS && priv->dp_out_buf_len > 0) { *((unsigned char *)(port->write_urb->transfer_buffer)) = (unsigned char)DIGI_CMD_SEND_DATA; @@ -1288,11 +1271,11 @@ static void digi_write_bulk_callback(struct urb *urb) __func__, ret, priv->dp_port_num); } -static int digi_write_room(struct usb_serial_port *port) +static int digi_write_room(struct tty_struct *tty) { - - int room; + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); + int room; unsigned long flags = 0; spin_lock_irqsave(&priv->dp_port_lock, flags); @@ -1308,12 +1291,11 @@ static int digi_write_room(struct usb_serial_port *port) } -static int digi_chars_in_buffer(struct usb_serial_port *port) +static int digi_chars_in_buffer(struct tty_struct *tty) { - + struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); - if (port->write_urb->status == -EINPROGRESS || priv->dp_write_urb_in_use) { dbg("digi_chars_in_buffer: port=%d, chars=%d", @@ -1329,7 +1311,8 @@ static int digi_chars_in_buffer(struct usb_serial_port *port) } -static int digi_open(struct usb_serial_port *port, struct file *filp) +static int digi_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { int ret; unsigned char buf[32]; @@ -1338,7 +1321,7 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) unsigned long flags = 0; dbg("digi_open: TOP: port=%d, open_count=%d", - priv->dp_port_num, port->open_count); + priv->dp_port_num, port->port.count); /* be sure the device is started up */ if (digi_startup_device(port->serial) != 0) @@ -1380,9 +1363,11 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) dbg("digi_open: write oob failed, ret=%d", ret); /* set termios settings */ - not_termios.c_cflag = ~port->tty->termios->c_cflag; - not_termios.c_iflag = ~port->tty->termios->c_iflag; - digi_set_termios(port, ¬_termios); + if (tty) { + not_termios.c_cflag = ~tty->termios->c_cflag; + not_termios.c_iflag = ~tty->termios->c_iflag; + digi_set_termios(tty, port, ¬_termios); + } /* set DTR and RTS */ digi_set_modem_signals(port, TIOCM_DTR|TIOCM_RTS, 1); @@ -1391,16 +1376,16 @@ static int digi_open(struct usb_serial_port *port, struct file *filp) } -static void digi_close(struct usb_serial_port *port, struct file *filp) +static void digi_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { DEFINE_WAIT(wait); int ret; unsigned char buf[32]; - struct tty_struct *tty = port->tty; struct digi_port *priv = usb_get_serial_port_data(port); dbg("digi_close: TOP: port=%d, open_count=%d", - priv->dp_port_num, port->open_count); + priv->dp_port_num, port->port.count); mutex_lock(&port->serial->disc_mutex); /* if disconnected, just clear flags */ @@ -1663,7 +1648,7 @@ static int digi_read_inb_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; struct digi_port *priv = usb_get_serial_port_data(port); int opcode = ((unsigned char *)urb->transfer_buffer)[0]; int len = ((unsigned char *)urb->transfer_buffer)[1]; @@ -1675,7 +1660,7 @@ static int digi_read_inb_callback(struct urb *urb) /* do not process callbacks on closed ports */ /* but do continue the read chain */ - if (port->open_count == 0) + if (port->port.count == 0) return 0; /* short/multiple packet check */ @@ -1785,17 +1770,17 @@ static int digi_read_oob_callback(struct urb *urb) if (val & DIGI_READ_INPUT_SIGNALS_CTS) { priv->dp_modem_signals |= TIOCM_CTS; /* port must be open to use tty struct */ - if (port->open_count - && port->tty->termios->c_cflag & CRTSCTS) { - port->tty->hw_stopped = 0; + if (port->port.count + && port->port.tty->termios->c_cflag & CRTSCTS) { + port->port.tty->hw_stopped = 0; digi_wakeup_write(port); } } else { priv->dp_modem_signals &= ~TIOCM_CTS; /* port must be open to use tty struct */ - if (port->open_count - && port->tty->termios->c_cflag & CRTSCTS) { - port->tty->hw_stopped = 1; + if (port->port.count + && port->port.tty->termios->c_cflag & CRTSCTS) { + port->port.tty->hw_stopped = 1; } } if (val & DIGI_READ_INPUT_SIGNALS_DSR) diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index c5ec309a3cb1..47ebdf5fad8e 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -31,7 +31,7 @@ * Moved MOD_DEC_USE_COUNT to end of empeg_close(). * * (12/03/2000) gb - * Added port->tty->ldisc.set_termios(port->tty, NULL) to empeg_open() + * Added port->port.tty->ldisc.set_termios(port->port.tty, NULL) to empeg_open() * This notifies the tty driver that the termios have changed. * * (11/13/2000) gb @@ -77,22 +77,18 @@ static int debug; #define EMPEG_PRODUCT_ID 0x0001 /* function prototypes for an empeg-car player */ -static int empeg_open (struct usb_serial_port *port, struct file *filp); -static void empeg_close (struct usb_serial_port *port, struct file *filp); -static int empeg_write (struct usb_serial_port *port, +static int empeg_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void empeg_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int empeg_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); -static int empeg_write_room (struct usb_serial_port *port); -static int empeg_chars_in_buffer (struct usb_serial_port *port); -static void empeg_throttle (struct usb_serial_port *port); -static void empeg_unthrottle (struct usb_serial_port *port); +static int empeg_write_room (struct tty_struct *tty); +static int empeg_chars_in_buffer (struct tty_struct *tty); +static void empeg_throttle (struct tty_struct *tty); +static void empeg_unthrottle (struct tty_struct *tty); static int empeg_startup (struct usb_serial *serial); static void empeg_shutdown (struct usb_serial *serial); -static int empeg_ioctl (struct usb_serial_port *port, - struct file * file, - unsigned int cmd, - unsigned long arg); -static void empeg_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); +static void empeg_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); static void empeg_write_bulk_callback (struct urb *urb); static void empeg_read_bulk_callback (struct urb *urb); @@ -125,7 +121,6 @@ static struct usb_serial_driver empeg_device = { .unthrottle = empeg_unthrottle, .attach = empeg_startup, .shutdown = empeg_shutdown, - .ioctl = empeg_ioctl, .set_termios = empeg_set_termios, .write = empeg_write, .write_room = empeg_write_room, @@ -145,7 +140,8 @@ static int bytes_out; /****************************************************************************** * Empeg specific driver functions ******************************************************************************/ -static int empeg_open (struct usb_serial_port *port, struct file *filp) +static int empeg_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { struct usb_serial *serial = port->serial; int result = 0; @@ -153,7 +149,7 @@ static int empeg_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __func__, port->number); /* Force default termio settings */ - empeg_set_termios (port, NULL) ; + empeg_set_termios (tty, port, NULL) ; bytes_in = 0; bytes_out = 0; @@ -178,7 +174,8 @@ static int empeg_open (struct usb_serial_port *port, struct file *filp) } -static void empeg_close (struct usb_serial_port *port, struct file * filp) +static void empeg_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file * filp) { dbg("%s - port %d", __func__, port->number); @@ -189,7 +186,7 @@ static void empeg_close (struct usb_serial_port *port, struct file * filp) } -static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int empeg_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct urb *urb; @@ -203,7 +200,6 @@ static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, dbg("%s - port %d", __func__, port->number); while (count > 0) { - /* try to find a free urb in our list of them */ urb = NULL; @@ -262,15 +258,14 @@ static int empeg_write (struct usb_serial_port *port, const unsigned char *buf, bytes_out += transfer_size; } - exit: return bytes_sent; - } -static int empeg_write_room (struct usb_serial_port *port) +static int empeg_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; int i; int room = 0; @@ -278,25 +273,22 @@ static int empeg_write_room (struct usb_serial_port *port) dbg("%s - port %d", __func__, port->number); spin_lock_irqsave (&write_urb_pool_lock, flags); - /* tally up the number of bytes available */ for (i = 0; i < NUM_URBS; ++i) { if (write_urb_pool[i]->status != -EINPROGRESS) { room += URB_TRANSFER_BUFFER_SIZE; } } - spin_unlock_irqrestore (&write_urb_pool_lock, flags); - dbg("%s - returns %d", __func__, room); - - return (room); + return room; } -static int empeg_chars_in_buffer (struct usb_serial_port *port) +static int empeg_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; int i; int chars = 0; @@ -356,7 +348,7 @@ static void empeg_read_bulk_callback (struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); @@ -386,27 +378,24 @@ static void empeg_read_bulk_callback (struct urb *urb) } -static void empeg_throttle (struct usb_serial_port *port) +static void empeg_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->read_urb); } -static void empeg_unthrottle (struct usb_serial_port *port) +static void empeg_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int result; - dbg("%s - port %d", __func__, port->number); port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); - - return; } @@ -436,17 +425,10 @@ static void empeg_shutdown (struct usb_serial *serial) } -static int empeg_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); - - return -ENOIOCTLCMD; -} - - -static void empeg_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void empeg_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; dbg("%s - port %d", __func__, port->number); /* @@ -491,8 +473,8 @@ static void empeg_set_termios (struct usb_serial_port *port, struct ktermios *ol * this is bad as it opens up the possibility of dropping bytes * on the floor. We don't want to drop bytes on the floor. :) */ - port->tty->low_latency = 1; - tty_encode_baud_rate(port->tty, 115200, 115200); + tty->low_latency = 1; + tty_encode_baud_rate(tty, 115200, 115200); } diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0ff4a3971e45..abbb447e5375 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -682,21 +682,21 @@ static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_i static void ftdi_shutdown (struct usb_serial *serial); static int ftdi_sio_port_probe (struct usb_serial_port *port); static int ftdi_sio_port_remove (struct usb_serial_port *port); -static int ftdi_open (struct usb_serial_port *port, struct file *filp); -static void ftdi_close (struct usb_serial_port *port, struct file *filp); -static int ftdi_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int ftdi_write_room (struct usb_serial_port *port); -static int ftdi_chars_in_buffer (struct usb_serial_port *port); +static int ftdi_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void ftdi_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int ftdi_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); +static int ftdi_write_room (struct tty_struct *tty); +static int ftdi_chars_in_buffer (struct tty_struct *tty); static void ftdi_write_bulk_callback (struct urb *urb); static void ftdi_read_bulk_callback (struct urb *urb); static void ftdi_process_read (struct work_struct *work); -static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file); -static int ftdi_tiocmset (struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear); -static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void ftdi_break_ctl (struct usb_serial_port *port, int break_state ); -static void ftdi_throttle (struct usb_serial_port *port); -static void ftdi_unthrottle (struct usb_serial_port *port); +static void ftdi_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); +static int ftdi_tiocmget (struct tty_struct *tty, struct file *file); +static int ftdi_tiocmset (struct tty_struct *tty, struct file * file, unsigned int set, unsigned int clear); +static int ftdi_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); +static void ftdi_break_ctl (struct tty_struct *tty, int break_state ); +static void ftdi_throttle (struct tty_struct *tty); +static void ftdi_unthrottle (struct tty_struct *tty); static unsigned short int ftdi_232am_baud_base_to_divisor (int baud, int base); static unsigned short int ftdi_232am_baud_to_divisor (int baud); @@ -843,42 +843,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, unsigned } -static __u32 get_ftdi_divisor(struct usb_serial_port * port); - - -static int change_speed(struct usb_serial_port *port) -{ - struct ftdi_private *priv = usb_get_serial_port_data(port); - char *buf; - __u16 urb_value; - __u16 urb_index; - __u32 urb_index_value; - int rv; - - buf = kmalloc(1, GFP_NOIO); - if (!buf) - return -ENOMEM; - - urb_index_value = get_ftdi_divisor(port); - urb_value = (__u16)urb_index_value; - urb_index = (__u16)(urb_index_value >> 16); - if (priv->interface) { /* FT2232C */ - urb_index = (__u16)((urb_index << 8) | priv->interface); - } - - rv = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - FTDI_SIO_SET_BAUDRATE_REQUEST, - FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, - urb_value, urb_index, - buf, 0, WDR_SHORT_TIMEOUT); - - kfree(buf); - return rv; -} - - -static __u32 get_ftdi_divisor(struct usb_serial_port * port) +static __u32 get_ftdi_divisor(struct tty_struct *tty, struct usb_serial_port *port) { /* get_ftdi_divisor */ struct ftdi_private *priv = usb_get_serial_port_data(port); __u32 div_value = 0; @@ -910,7 +875,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) /* 1. Get the baud rate from the tty settings, this observes alt_speed hack */ - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); dbg("%s - tty_get_baud_rate reports speed %d", __func__, baud); /* 2. Observe async-compatible custom_divisor hack, update baudrate if needed */ @@ -976,10 +941,42 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) ftdi_chip_name[priv->chip_type]); } - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); return(div_value); } +static int change_speed(struct tty_struct *tty, struct usb_serial_port *port) +{ + struct ftdi_private *priv = usb_get_serial_port_data(port); + char *buf; + __u16 urb_value; + __u16 urb_index; + __u32 urb_index_value; + int rv; + + buf = kmalloc(1, GFP_NOIO); + if (!buf) + return -ENOMEM; + + urb_index_value = get_ftdi_divisor(tty, port); + urb_value = (__u16)urb_index_value; + urb_index = (__u16)(urb_index_value >> 16); + if (priv->interface) { /* FT2232C */ + urb_index = (__u16)((urb_index << 8) | priv->interface); + } + + rv = usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + FTDI_SIO_SET_BAUDRATE_REQUEST, + FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, + urb_value, urb_index, + buf, 0, WDR_SHORT_TIMEOUT); + + kfree(buf); + return rv; +} + + static int get_serial_info(struct usb_serial_port * port, struct serial_struct __user * retinfo) { @@ -998,7 +995,8 @@ static int get_serial_info(struct usb_serial_port * port, struct serial_struct _ } /* get_serial_info */ -static int set_serial_info(struct usb_serial_port * port, struct serial_struct __user * newinfo) +static int set_serial_info(struct tty_struct *tty, + struct usb_serial_port * port, struct serial_struct __user * newinfo) { /* set_serial_info */ struct ftdi_private *priv = usb_get_serial_port_data(port); struct serial_struct new_serial; @@ -1030,30 +1028,29 @@ static int set_serial_info(struct usb_serial_port * port, struct serial_struct _ (new_serial.flags & ASYNC_FLAGS)); priv->custom_divisor = new_serial.custom_divisor; - port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: if ((old_priv.flags & ASYNC_SPD_MASK) != (priv->flags & ASYNC_SPD_MASK)) { if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - port->tty->alt_speed = 57600; + tty->alt_speed = 57600; else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - port->tty->alt_speed = 115200; + tty->alt_speed = 115200; else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - port->tty->alt_speed = 230400; + tty->alt_speed = 230400; else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - port->tty->alt_speed = 460800; + tty->alt_speed = 460800; else - port->tty->alt_speed = 0; + tty->alt_speed = 0; } if (((old_priv.flags & ASYNC_SPD_MASK) != (priv->flags & ASYNC_SPD_MASK)) || (((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) && (old_priv.custom_divisor != priv->custom_divisor))) { - change_speed(port); + change_speed(tty, port); } - - return (0); + return 0; } /* set_serial_info */ @@ -1415,7 +1412,8 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) return 0; } -static int ftdi_open (struct usb_serial_port *port, struct file *filp) +static int ftdi_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { /* ftdi_open */ struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1433,8 +1431,8 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) priv->rx_bytes = 0; spin_unlock_irqrestore(&priv->rx_lock, flags); - if (port->tty) - port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + if (tty) + tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; /* No error checking for this (will get errors later anyway) */ /* See ftdi_sio.h for description of what is reset */ @@ -1448,8 +1446,8 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) This is same behaviour as serial.c/rs_open() - Kuba */ /* ftdi_set_termios will send usb control messages */ - if (port->tty) - ftdi_set_termios(port, port->tty->termios); + if (tty) + ftdi_set_termios(tty, port, tty->termios); /* FIXME: Flow control might be enabled, so it should be checked - we have no control of defaults! */ @@ -1485,9 +1483,10 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) * */ -static void ftdi_close (struct usb_serial_port *port, struct file *filp) +static void ftdi_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { /* ftdi_close */ - unsigned int c_cflag = port->tty->termios->c_cflag; + unsigned int c_cflag = tty->termios->c_cflag; struct ftdi_private *priv = usb_get_serial_port_data(port); char buf[1]; @@ -1527,7 +1526,7 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp) * * The new devices do not require this byte */ -static int ftdi_write (struct usb_serial_port *port, +static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { /* ftdi_write */ struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -1686,8 +1685,9 @@ static void ftdi_write_bulk_callback (struct urb *urb) } /* ftdi_write_bulk_callback */ -static int ftdi_write_room( struct usb_serial_port *port ) +static int ftdi_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); int room; unsigned long flags; @@ -1707,11 +1707,11 @@ static int ftdi_write_room( struct usb_serial_port *port ) } spin_unlock_irqrestore(&priv->tx_lock, flags); return room; -} /* ftdi_write_room */ - +} -static int ftdi_chars_in_buffer (struct usb_serial_port *port) -{ /* ftdi_chars_in_buffer */ +static int ftdi_chars_in_buffer(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); int buffered; unsigned long flags; @@ -1726,12 +1726,10 @@ static int ftdi_chars_in_buffer (struct usb_serial_port *port) buffered = 0; } return buffered; -} /* ftdi_chars_in_buffer */ - - +} -static void ftdi_read_bulk_callback (struct urb *urb) -{ /* ftdi_read_bulk_callback */ +static void ftdi_read_bulk_callback(struct urb *urb) +{ struct usb_serial_port *port = urb->context; struct tty_struct *tty; struct ftdi_private *priv; @@ -1747,10 +1745,10 @@ static void ftdi_read_bulk_callback (struct urb *urb) dbg("%s - port %d", __func__, port->number); - if (port->open_count <= 0) + if (port->port.count <= 0) return; - tty = port->tty; + tty = port->port.tty; if (!tty) { dbg("%s - bad tty pointer - exiting",__func__); return; @@ -1803,10 +1801,10 @@ static void ftdi_process_read (struct work_struct *work) dbg("%s - port %d", __func__, port->number); - if (port->open_count <= 0) + if (port->port.count <= 0) return; - tty = port->tty; + tty = port->port.tty; if (!tty) { dbg("%s - bad tty pointer - exiting",__func__); return; @@ -1954,7 +1952,7 @@ static void ftdi_process_read (struct work_struct *work) } spin_unlock_irqrestore(&priv->rx_lock, flags); /* if the port is closed stop trying to read */ - if (port->open_count > 0){ + if (port->port.count > 0){ /* delay processing of remainder */ schedule_delayed_work(&priv->rx_work, 1); } else { @@ -1967,7 +1965,7 @@ static void ftdi_process_read (struct work_struct *work) priv->rx_processed = 0; /* if the port is closed stop trying to read */ - if (port->open_count > 0){ + if (port->port.count > 0){ /* Continue trying to always read */ usb_fill_bulk_urb(port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), @@ -1983,8 +1981,9 @@ static void ftdi_process_read (struct work_struct *work) } /* ftdi_process_read */ -static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ) +static void ftdi_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); __u16 urb_value = 0; char buf[1]; @@ -2018,11 +2017,12 @@ static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ) * WARNING: set_termios calls this with old_termios in kernel space */ -static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void ftdi_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { /* ftdi_termios */ struct usb_device *dev = port->serial->dev; struct ftdi_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; __u16 urb_value; /* will hold the new flags */ char buf[1]; /* Perhaps I should dynamically alloc this? */ @@ -2037,7 +2037,7 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old /* Force baud rate if this device requires it, unless it is set to B0. */ if (priv->force_baud && ((termios->c_cflag & CBAUD) != B0)) { dbg("%s: forcing baud rate for this device", __func__); - tty_encode_baud_rate(port->tty, priv->force_baud, + tty_encode_baud_rate(tty, priv->force_baud, priv->force_baud); } @@ -2104,7 +2104,7 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); } else { /* set the baudrate determined before */ - if (change_speed(port)) { + if (change_speed(tty, port)) { err("%s urb failed to set baudrate", __func__); } /* Ensure RTS and DTR are raised when baudrate changed from 0 */ @@ -2168,11 +2168,11 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct ktermios *old } return; -} /* ftdi_termios */ - +} -static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file) +static int ftdi_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned char buf[2]; int ret; @@ -2221,15 +2221,18 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file) priv->last_dtr_rts; } -static int ftdi_tiocmset(struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear) +static int ftdi_tiocmset(struct tty_struct *tty, struct file * file, + unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; dbg("%s TIOCMSET", __func__); return update_mctrl(port, set, clear); } -static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int ftdi_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); dbg("%s cmd 0x%04x", __func__, cmd); @@ -2241,7 +2244,7 @@ static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigne return get_serial_info(port, (struct serial_struct __user *) arg); case TIOCSSERIAL: /* sets serial port data */ - return set_serial_info(port, (struct serial_struct __user *) arg); + return set_serial_info(tty, port, (struct serial_struct __user *) arg); /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change @@ -2280,25 +2283,20 @@ static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigne */ } } - return(0); - break; + return 0; default: break; - } - - /* This is not necessarily an error - turns out the higher layers will do * some ioctls itself (see comment above) */ dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", __func__, cmd); + return -ENOIOCTLCMD; +} - return(-ENOIOCTLCMD); -} /* ftdi_ioctl */ - - -static void ftdi_throttle (struct usb_serial_port *port) +static void ftdi_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -2310,8 +2308,9 @@ static void ftdi_throttle (struct usb_serial_port *port) } -static void ftdi_unthrottle (struct usb_serial_port *port) +static void ftdi_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); int actually_throttled; unsigned long flags; diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 8ce5a56a48e3..06cfa43c6f02 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -275,7 +275,7 @@ static inline int isAbortTrfCmnd(const unsigned char *buf) static void send_to_tty(struct usb_serial_port *port, char *data, unsigned int actual_length) { - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; if (tty && actual_length) { @@ -970,7 +970,8 @@ static int garmin_init_session(struct usb_serial_port *port) -static int garmin_open (struct usb_serial_port *port, struct file *filp) +static int garmin_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { unsigned long flags; int status = 0; @@ -983,8 +984,8 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp) * through, otherwise it is scheduled, and with high data rates (like * with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->mode = initial_mode; @@ -998,17 +999,16 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp) usb_kill_urb (port->write_urb); usb_kill_urb (port->read_urb); - if (garmin_data_p->state == STATE_RESET) { + if (garmin_data_p->state == STATE_RESET) status = garmin_init_session(port); - } garmin_data_p->state = STATE_ACTIVE; - return status; } -static void garmin_close (struct usb_serial_port *port, struct file * filp) +static void garmin_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct usb_serial *serial = port->serial; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); @@ -1042,7 +1042,6 @@ static void garmin_close (struct usb_serial_port *port, struct file * filp) mutex_unlock(&port->serial->disc_mutex); } - static void garmin_write_bulk_callback (struct urb *urb) { unsigned long flags; @@ -1145,10 +1144,8 @@ static int garmin_write_bulk (struct usb_serial_port *port, return count; } - - -static int garmin_write (struct usb_serial_port *port, - const unsigned char *buf, int count) +static int garmin_write (struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { int pktid, pktsiz, len; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); @@ -1158,7 +1155,6 @@ static int garmin_write (struct usb_serial_port *port, /* check for our private packets */ if (count >= GARMIN_PKTHDR_LENGTH) { - len = PRIVPKTSIZ; if (count < len) len = count; @@ -1226,8 +1222,9 @@ static int garmin_write (struct usb_serial_port *port, } -static int garmin_write_room (struct usb_serial_port *port) +static int garmin_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; /* * Report back the bytes currently available in the output buffer. */ @@ -1236,20 +1233,6 @@ static int garmin_write_room (struct usb_serial_port *port) } -static int garmin_chars_in_buffer (struct usb_serial_port *port) -{ - /* - * Report back the number of bytes currently in our input buffer. - * Will this lock up the driver - the buffer contains an incomplete - * package which will not be written to the device until it - * has been completed ? - */ - //struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); - //return garmin_data_p->insize; - return 0; -} - - static void garmin_read_process(struct garmin_data * garmin_data_p, unsigned char *data, unsigned data_length) { @@ -1468,10 +1451,11 @@ static int garmin_flush_queue(struct garmin_data * garmin_data_p) } -static void garmin_throttle (struct usb_serial_port *port) +static void garmin_throttle(struct tty_struct *tty) { - unsigned long flags; + struct usb_serial_port *port = tty->driver_data; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + unsigned long flags; dbg("%s - port %d", __func__, port->number); /* set flag, data received will be put into a queue @@ -1482,10 +1466,11 @@ static void garmin_throttle (struct usb_serial_port *port) } -static void garmin_unthrottle (struct usb_serial_port *port) +static void garmin_unthrottle (struct tty_struct *tty) { - unsigned long flags; + struct usb_serial_port *port = tty->driver_data; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + unsigned long flags; int status; dbg("%s - port %d", __func__, port->number); @@ -1507,8 +1492,6 @@ static void garmin_unthrottle (struct usb_serial_port *port) } } - - /* * The timer is currently only used to send queued packets to * the tty in cases where the protocol provides no own handshaking @@ -1526,7 +1509,7 @@ static void timeout_handler(unsigned long data) -static int garmin_attach (struct usb_serial *serial) +static int garmin_attach(struct usb_serial *serial) { int status = 0; struct usb_serial_port *port = serial->port[0]; @@ -1556,7 +1539,7 @@ static int garmin_attach (struct usb_serial *serial) } -static void garmin_shutdown (struct usb_serial *serial) +static void garmin_shutdown(struct usb_serial *serial) { struct usb_serial_port *port = serial->port[0]; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); @@ -1588,7 +1571,6 @@ static struct usb_serial_driver garmin_device = { .shutdown = garmin_shutdown, .write = garmin_write, .write_room = garmin_write_room, - .chars_in_buffer = garmin_chars_in_buffer, .write_bulk_callback = garmin_write_bulk_callback, .read_bulk_callback = garmin_read_bulk_callback, .read_int_callback = garmin_read_int_callback, diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 537f12a027c2..5128018c2766 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -112,7 +112,8 @@ void usb_serial_generic_deregister (void) #endif } -int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) +int usb_serial_generic_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; int result = 0; @@ -123,8 +124,8 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) /* force low_latency on so that our tty_push actually forces the data through, otherwise it is scheduled, and with high data rates (like with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* clear the throttle flags */ spin_lock_irqsave(&port->lock, flags); @@ -152,7 +153,7 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) } EXPORT_SYMBOL_GPL(usb_serial_generic_open); -static void generic_cleanup (struct usb_serial_port *port) +static void generic_cleanup(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; @@ -182,7 +183,7 @@ int usb_serial_generic_resume(struct usb_serial *serial) #endif for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; - if (port->open_count && port->read_urb) { + if (port->port.count && port->read_urb) { r = usb_submit_urb(port->read_urb, GFP_NOIO); if (r < 0) c++; @@ -192,13 +193,15 @@ int usb_serial_generic_resume(struct usb_serial *serial) return c ? -EIO : 0; } -void usb_serial_generic_close (struct usb_serial_port *port, struct file * filp) +void usb_serial_generic_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { dbg("%s - port %d", __func__, port->number); generic_cleanup (port); } -int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char *buf, int count) +int usb_serial_generic_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; int result; @@ -255,8 +258,9 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char * return 0; } -int usb_serial_generic_write_room (struct usb_serial_port *port) +int usb_serial_generic_write_room (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int room = 0; @@ -272,8 +276,9 @@ int usb_serial_generic_write_room (struct usb_serial_port *port) return room; } -int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port) +int usb_serial_generic_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int chars = 0; @@ -286,7 +291,7 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port) } dbg("%s - returns %d", __func__, chars); - return (chars); + return chars; } @@ -311,10 +316,10 @@ static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) } /* Push data to tty layer and resubmit the bulk read URB */ -static void flush_and_resubmit_read_urb (struct usb_serial_port *port) +static void flush_and_resubmit_read_urb(struct usb_serial_port *port) { struct urb *urb = port->read_urb; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; int room; /* Push data to tty */ @@ -329,7 +334,7 @@ static void flush_and_resubmit_read_urb (struct usb_serial_port *port) resubmit_read_urb(port, GFP_ATOMIC); } -void usb_serial_generic_read_bulk_callback (struct urb *urb) +void usb_serial_generic_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; @@ -357,7 +362,7 @@ void usb_serial_generic_read_bulk_callback (struct urb *urb) } EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); -void usb_serial_generic_write_bulk_callback (struct urb *urb) +void usb_serial_generic_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int status = urb->status; @@ -374,8 +379,9 @@ void usb_serial_generic_write_bulk_callback (struct urb *urb) } EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); -void usb_serial_generic_throttle (struct usb_serial_port *port) +void usb_serial_generic_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; dbg("%s - port %d", __func__, port->number); @@ -387,8 +393,9 @@ void usb_serial_generic_throttle (struct usb_serial_port *port) spin_unlock_irqrestore(&port->lock, flags); } -void usb_serial_generic_unthrottle (struct usb_serial_port *port) +void usb_serial_generic_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int was_throttled; unsigned long flags; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 2fd449bcfa35..29346d79df7b 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -206,18 +206,18 @@ static void edge_bulk_out_data_callback (struct urb *urb); static void edge_bulk_out_cmd_callback (struct urb *urb); /* function prototypes for the usbserial callbacks */ -static int edge_open (struct usb_serial_port *port, struct file *filp); -static void edge_close (struct usb_serial_port *port, struct file *filp); -static int edge_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int edge_write_room (struct usb_serial_port *port); -static int edge_chars_in_buffer (struct usb_serial_port *port); -static void edge_throttle (struct usb_serial_port *port); -static void edge_unthrottle (struct usb_serial_port *port); -static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); -static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg); -static void edge_break (struct usb_serial_port *port, int break_state); -static int edge_tiocmget (struct usb_serial_port *port, struct file *file); -static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); +static int edge_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void edge_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int edge_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); +static int edge_write_room (struct tty_struct *tty); +static int edge_chars_in_buffer (struct tty_struct *tty); +static void edge_throttle (struct tty_struct *tty); +static void edge_unthrottle (struct tty_struct *tty); +static void edge_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); +static int edge_ioctl (struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static void edge_break (struct tty_struct *tty, int break_state); +static int edge_tiocmget (struct tty_struct *tty, struct file *file); +static int edge_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int edge_startup (struct usb_serial *serial); static void edge_shutdown (struct usb_serial *serial); @@ -233,7 +233,8 @@ static void handle_new_lsr (struct edgeport_port *edge_port, __u8 lsrData, __u8 static int send_iosp_ext_cmd (struct edgeport_port *edge_port, __u8 command, __u8 param); static int calc_baud_rate_divisor (int baud_rate, int *divisor); static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRate); -static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios); +static void change_port_settings (struct tty_struct *tty, struct edgeport_port *edge_port, + struct ktermios *old_termios); static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 regNum, __u8 regValue); static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer, int writeLength); static void send_more_port_data (struct edgeport_serial *edge_serial, struct edgeport_port *edge_port); @@ -639,8 +640,8 @@ static void edge_interrupt_callback (struct urb *urb) dbg("%s - txcredits for port%d = %d", __func__, portNumber, edge_port->txCredits); /* tell the tty driver that something has changed */ - if (edge_port->port->tty) - tty_wakeup(edge_port->port->tty); + if (edge_port->port->port.tty) + tty_wakeup(edge_port->port->port.tty); // Since we have more credit, check if more data can be sent send_more_port_data(edge_serial, edge_port); @@ -737,7 +738,7 @@ static void edge_bulk_out_data_callback (struct urb *urb) __func__, status); } - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; if (tty && edge_port->open) { /* let the tty driver wakeup if it has a special write_wakeup function */ @@ -781,7 +782,7 @@ static void edge_bulk_out_cmd_callback (struct urb *urb) } /* Get pointer to tty */ - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; /* tell the tty driver that something has changed */ if (tty && edge_port->open) @@ -803,7 +804,8 @@ static void edge_bulk_out_cmd_callback (struct urb *urb) * If successful, we return 0 * Otherwise we return a negative error number. *****************************************************************************/ -static int edge_open (struct usb_serial_port *port, struct file * filp) +static int edge_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct usb_serial *serial; @@ -815,16 +817,15 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) if (edge_port == NULL) return -ENODEV; - if (port->tty) - port->tty->low_latency = low_latency; + if (tty) + tty->low_latency = low_latency; /* see if we've set up our endpoint info yet (can't set it up in edge_startup as the structures were not set up at that time.) */ serial = port->serial; edge_serial = usb_get_serial_data(serial); - if (edge_serial == NULL) { + if (edge_serial == NULL) return -ENODEV; - } if (edge_serial->interrupt_in_buffer == NULL) { struct usb_serial_port *port0 = serial->port[0]; @@ -908,7 +909,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) if (!edge_port->txfifo.fifo) { dbg("%s - no memory", __func__); - edge_close (port, filp); + edge_close (tty, port, filp); return -ENOMEM; } @@ -918,7 +919,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) if (!edge_port->write_urb) { dbg("%s - no memory", __func__); - edge_close (port, filp); + edge_close (tty, port, filp); return -ENOMEM; } @@ -1038,7 +1039,8 @@ static void block_until_tx_empty (struct edgeport_port *edge_port) * edge_close * this function is called by the tty driver when a port is closed *****************************************************************************/ -static void edge_close (struct usb_serial_port *port, struct file * filp) +static void edge_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; @@ -1106,7 +1108,8 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) * If successful, we return the number of bytes written, otherwise we return * a negative error number. *****************************************************************************/ -static int edge_write (struct usb_serial_port *port, const unsigned char *data, int count) +static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct TxFifo *fifo; @@ -1308,8 +1311,9 @@ exit_send: * (the txCredits), * Otherwise we return a negative error number. *****************************************************************************/ -static int edge_write_room (struct usb_serial_port *port) +static int edge_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int room; unsigned long flags; @@ -1347,8 +1351,9 @@ static int edge_write_room (struct usb_serial_port *port) * system, * Otherwise we return a negative error number. *****************************************************************************/ -static int edge_chars_in_buffer (struct usb_serial_port *port) +static int edge_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int num_chars; unsigned long flags; @@ -1381,10 +1386,10 @@ static int edge_chars_in_buffer (struct usb_serial_port *port) * this function is called by the tty driver when it wants to stop the data * being read from the port. *****************************************************************************/ -static void edge_throttle (struct usb_serial_port *port) +static void edge_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -1397,16 +1402,10 @@ static void edge_throttle (struct usb_serial_port *port) return; } - tty = port->tty; - if (!tty) { - dbg ("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = edge_write (port, &stop_char, 1); + status = edge_write (tty, port, &stop_char, 1); if (status <= 0) { return; } @@ -1430,10 +1429,10 @@ static void edge_throttle (struct usb_serial_port *port) * this function is called by the tty driver when it wants to resume the data * being read from the port (called after SerialThrottle is called) *****************************************************************************/ -static void edge_unthrottle (struct usb_serial_port *port) +static void edge_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -1446,31 +1445,18 @@ static void edge_unthrottle (struct usb_serial_port *port) return; } - tty = port->tty; - if (!tty) { - dbg ("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = edge_write (port, &start_char, 1); - if (status <= 0) { + status = edge_write(tty, port, &start_char, 1); + if (status <= 0) return; - } } - /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { edge_port->shadowMCR |= MCR_RTS; - status = send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR); - if (status != 0) { - return; - } + send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR); } - - return; } @@ -1478,11 +1464,10 @@ static void edge_unthrottle (struct usb_serial_port *port) * SerialSetTermios * this function is called by the tty driver when it wants to change the termios structure *****************************************************************************/ -static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void edge_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { - /* FIXME: This function appears unused ?? */ struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned int cflag; cflag = tty->termios->c_cflag; @@ -1502,9 +1487,7 @@ static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old } /* change the port settings to the new ones specified */ - change_port_settings (edge_port, old_termios); - - return; + change_port_settings(tty, edge_port, old_termios); } @@ -1536,25 +1519,9 @@ static int get_lsr_info(struct edgeport_port *edge_port, unsigned int __user *va return 0; } -static int get_number_bytes_avail(struct edgeport_port *edge_port, unsigned int __user *value) -{ - unsigned int result = 0; - struct tty_struct *tty = edge_port->port->tty; - - if (!tty) - return -ENOIOCTLCMD; - - result = tty->read_cnt; - - dbg("%s(%d) = %d", __func__, edge_port->port->number, result); - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - //return 0; - return -ENOIOCTLCMD; -} - -static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear) +static int edge_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int mcr; @@ -1582,8 +1549,9 @@ static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsig return 0; } -static int edge_tiocmget(struct usb_serial_port *port, struct file *file) +static int edge_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int result = 0; unsigned int msr; @@ -1624,9 +1592,6 @@ static int get_serial_info(struct edgeport_port *edge_port, struct serial_struct tmp.baud_base = 9600; tmp.close_delay = 5*HZ; tmp.closing_wait = 30*HZ; -// tmp.custom_divisor = state->custom_divisor; -// tmp.hub6 = state->hub6; -// tmp.io_type = state->io_type; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -1639,8 +1604,10 @@ static int get_serial_info(struct edgeport_port *edge_port, struct serial_struct * SerialIoctl * this function handles any ioctl calls to the driver *****************************************************************************/ -static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) +static int edge_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; DEFINE_WAIT(wait); struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct async_icount cnow; @@ -1650,25 +1617,14 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); switch (cmd) { - // return number of bytes available - case TIOCINQ: - dbg("%s (%d) TIOCINQ", __func__, port->number); - return get_number_bytes_avail(edge_port, (unsigned int __user *) arg); - break; - case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); return get_lsr_info(edge_port, (unsigned int __user *) arg); - return 0; case TIOCGSERIAL: dbg("%s (%d) TIOCGSERIAL", __func__, port->number); return get_serial_info(edge_port, (struct serial_struct __user *) arg); - case TIOCSSERIAL: - dbg("%s (%d) TIOCSSERIAL", __func__, port->number); - break; - case TIOCMIWAIT: dbg("%s (%d) TIOCMIWAIT", __func__, port->number); cprev = edge_port->icount; @@ -1723,8 +1679,9 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned * SerialBreak * this function sends a break to the port *****************************************************************************/ -static void edge_break (struct usb_serial_port *port, int break_state) +static void edge_break (struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial); int status; @@ -1866,7 +1823,7 @@ static void process_rcvd_data (struct edgeport_serial *edge_serial, unsigned cha port = edge_serial->serial->port[edge_serial->rxPort]; edge_port = usb_get_serial_port_data(port); if (edge_port->open) { - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; if (tty) { dbg("%s - Sending %d bytes to TTY for port %d", __func__, rxLen, edge_serial->rxPort); edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen); @@ -1941,8 +1898,9 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 handle_new_msr (edge_port, byte2); /* send the current line settings to the port so we are in sync with any further termios calls */ - if (edge_port->port->tty) - change_port_settings (edge_port, edge_port->port->tty->termios); + /* FIXME: locking on tty */ + if (edge_port->port->port.tty) + change_port_settings(edge_port->port->port.tty, edge_port, edge_port->port->port.tty->termios); /* we have completed the open */ edge_port->openPending = false; @@ -2078,8 +2036,8 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, __u8 l } /* Place LSR data byte into Rx buffer */ - if (lsrData && edge_port->port->tty) - edge_tty_recv(&edge_port->port->dev, edge_port->port->tty, &data, 1); + if (lsrData && edge_port->port->port.tty) + edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1); /* update input line counters */ icount = &edge_port->icount; @@ -2473,13 +2431,11 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r * This routine is called to set the UART on the device to match the specified * new settings. *****************************************************************************/ -#ifndef CMSPAR -#define CMSPAR 0 -#endif -static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios) + +static void change_port_settings(struct tty_struct *tty, + struct edgeport_port *edge_port, struct ktermios *old_termios) { struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); - struct tty_struct *tty; int baud; unsigned cflag; __u8 mask = 0xff; @@ -2498,13 +2454,6 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi return; } - tty = edge_port->port->tty; - if ((!tty) || - (!tty->termios)) { - dbg("%s - no tty structures", __func__); - return; - } - cflag = tty->termios->c_cflag; switch (cflag & CSIZE) { diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index a58822a14a87..7cf383a2a556 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -243,9 +243,9 @@ static void edge_tty_recv(struct device *dev, struct tty_struct *tty, static void stop_read(struct edgeport_port *edge_port); static int restart_read(struct edgeport_port *edge_port); -static void edge_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios); -static void edge_send(struct usb_serial_port *port); +static void edge_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); +static void edge_send(struct tty_struct *tty); /* sysfs attributes */ static int edge_create_sysfs_attrs(struct usb_serial_port *port); @@ -572,7 +572,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, int flush) { int baud_rate; - struct tty_struct *tty = port->port->tty; + struct tty_struct *tty = port->port->port.tty; wait_queue_t wait; unsigned long flags; @@ -1554,7 +1554,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) /* Save the new modem status */ edge_port->shadow_msr = msr & 0xf0; - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; /* handle CTS flow control */ if (tty && C_CRTSCTS(tty)) { if (msr & EDGEPORT_MSR_CTS) { @@ -1587,9 +1587,8 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK); /* Place LSR data byte into Rx buffer */ - if (lsr_data && edge_port->port->tty) - edge_tty_recv(&edge_port->port->dev, edge_port->port->tty, - &data, 1); + if (lsr_data && edge_port->port->port.tty) + edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1); /* update input line counters */ icount = &edge_port->icount; @@ -1750,7 +1749,7 @@ static void edge_bulk_in_callback(struct urb *urb) ++data; } - tty = edge_port->port->tty; + tty = edge_port->port->port.tty; if (tty && urb->actual_length) { usb_serial_debug_data(debug, &edge_port->port->dev, __func__, urb->actual_length, data); @@ -1819,10 +1818,11 @@ static void edge_bulk_out_callback(struct urb *urb) } /* send any buffered data */ - edge_send(port); + edge_send(port->port.tty); } -static int edge_open(struct usb_serial_port *port, struct file *filp) +static int edge_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct edgeport_serial *edge_serial; @@ -1838,7 +1838,8 @@ static int edge_open(struct usb_serial_port *port, struct file *filp) if (edge_port == NULL) return -ENODEV; - port->tty->low_latency = low_latency; + if (tty) + tty->low_latency = low_latency; port_number = port->number - port->serial->minor; switch (port_number) { @@ -1874,7 +1875,8 @@ static int edge_open(struct usb_serial_port *port, struct file *filp) } /* set up the port settings */ - edge_set_termios(port, port->tty->termios); + if (tty) + edge_set_termios(tty, port, port->port.tty->termios); /* open up the port */ @@ -2000,7 +2002,8 @@ release_es_lock: return status; } -static void edge_close(struct usb_serial_port *port, struct file *filp) +static void edge_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct edgeport_serial *edge_serial; struct edgeport_port *edge_port; @@ -2048,8 +2051,8 @@ static void edge_close(struct usb_serial_port *port, struct file *filp) dbg("%s - exited", __func__); } -static int edge_write(struct usb_serial_port *port, const unsigned char *data, - int count) +static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned long flags; @@ -2070,16 +2073,16 @@ static int edge_write(struct usb_serial_port *port, const unsigned char *data, count = edge_buf_put(edge_port->ep_out_buf, data, count); spin_unlock_irqrestore(&edge_port->ep_lock, flags); - edge_send(port); + edge_send(tty); return count; } -static void edge_send(struct usb_serial_port *port) +static void edge_send(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int count, result; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned long flags; @@ -2133,8 +2136,9 @@ static void edge_send(struct usb_serial_port *port) tty_wakeup(tty); } -static int edge_write_room(struct usb_serial_port *port) +static int edge_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -2154,8 +2158,9 @@ static int edge_write_room(struct usb_serial_port *port) return room; } -static int edge_chars_in_buffer(struct usb_serial_port *port) +static int edge_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -2175,10 +2180,10 @@ static int edge_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void edge_throttle(struct usb_serial_port *port) +static void edge_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; int status; dbg("%s - port %d", __func__, port->number); @@ -2189,11 +2194,10 @@ static void edge_throttle(struct usb_serial_port *port) /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = edge_write(port, &stop_char, 1); - if (status <= 0) - dev_err(&port->dev, - "%s - failed to write stop character, %d\n", - __func__, status); + status = edge_write(tty, port, &stop_char, 1); + if (status <= 0) { + dev_err(&port->dev, "%s - failed to write stop character, %d\n", __func__, status); + } } /* if we are implementing RTS/CTS, stop reads */ @@ -2203,10 +2207,10 @@ static void edge_throttle(struct usb_serial_port *port) } -static void edge_unthrottle(struct usb_serial_port *port) +static void edge_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; int status; dbg("%s - port %d", __func__, port->number); @@ -2217,11 +2221,10 @@ static void edge_unthrottle(struct usb_serial_port *port) /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = edge_write(port, &start_char, 1); - if (status <= 0) - dev_err(&port->dev, - "%s - failed to write start character, %d\n", - __func__, status); + status = edge_write(tty, port, &start_char, 1); + if (status <= 0) { + dev_err(&port->dev, "%s - failed to write start character, %d\n", __func__, status); + } } /* if we are implementing RTS/CTS, restart reads */ /* are the Edgeport will assert the RTS line */ @@ -2271,11 +2274,10 @@ static int restart_read(struct edgeport_port *edge_port) return status; } -static void change_port_settings(struct edgeport_port *edge_port, - struct ktermios *old_termios) +static void change_port_settings(struct tty_struct *tty, + struct edgeport_port *edge_port, struct ktermios *old_termios) { struct ump_uart_config *config; - struct tty_struct *tty; int baud; unsigned cflag; int status; @@ -2284,9 +2286,7 @@ static void change_port_settings(struct edgeport_port *edge_port, dbg("%s - port %d", __func__, edge_port->port->number); - tty = edge_port->port->tty; - - config = kmalloc(sizeof(*config), GFP_KERNEL); + config = kmalloc (sizeof (*config), GFP_KERNEL); if (!config) { *tty->termios = *old_termios; dev_err(&edge_port->port->dev, "%s - out of memory\n", @@ -2419,11 +2419,13 @@ static void change_port_settings(struct edgeport_port *edge_port, return; } -static void edge_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void edge_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; + unsigned int cflag; + + cflag = tty->termios->c_cflag; dbg("%s - clfag %08x iflag %08x", __func__, tty->termios->c_cflag, tty->termios->c_iflag); @@ -2434,12 +2436,14 @@ static void edge_set_termios(struct usb_serial_port *port, if (edge_port == NULL) return; /* change the port settings to the new ones specified */ - change_port_settings(edge_port, old_termios); + change_port_settings(tty, edge_port, old_termios); + return; } -static int edge_tiocmset(struct usb_serial_port *port, struct file *file, +static int edge_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int mcr; unsigned long flags; @@ -2469,8 +2473,9 @@ static int edge_tiocmset(struct usb_serial_port *port, struct file *file, return 0; } -static int edge_tiocmget(struct usb_serial_port *port, struct file *file) +static int edge_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); unsigned int result = 0; unsigned int msr; @@ -2522,9 +2527,10 @@ static int get_serial_info(struct edgeport_port *edge_port, return 0; } -static int edge_ioctl(struct usb_serial_port *port, struct file *file, +static int edge_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct async_icount cnow; struct async_icount cprev; @@ -2569,18 +2575,19 @@ static int edge_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static void edge_break(struct usb_serial_port *port, int on) +static void edge_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int status; int bv = 0; /* Off */ - dbg("%s - state = %d", __func__, on); + dbg("%s - state = %d", __func__, break_state); /* chase the port close */ chase_port(edge_port, 0, 0); - if (on == -1) + if (break_state == -1) bv = 1; /* On */ status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv); if (status) diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 80d9ec5570d6..a7784642d6a1 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -74,19 +74,21 @@ static int connect_retries = KP_RETRIES; static int initial_wait; /* Function prototypes for an ipaq */ -static int ipaq_open (struct usb_serial_port *port, struct file *filp); -static void ipaq_close (struct usb_serial_port *port, struct file *filp); -static int ipaq_startup (struct usb_serial *serial); -static void ipaq_shutdown (struct usb_serial *serial); -static int ipaq_write(struct usb_serial_port *port, const unsigned char *buf, - int count); +static int ipaq_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void ipaq_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static int ipaq_startup(struct usb_serial *serial); +static void ipaq_shutdown(struct usb_serial *serial); +static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); static int ipaq_write_bulk(struct usb_serial_port *port, const unsigned char *buf, - int count); + int count); static void ipaq_write_gather(struct usb_serial_port *port); static void ipaq_read_bulk_callback (struct urb *urb); static void ipaq_write_bulk_callback(struct urb *urb); -static int ipaq_write_room(struct usb_serial_port *port); -static int ipaq_chars_in_buffer(struct usb_serial_port *port); +static int ipaq_write_room(struct tty_struct *tty); +static int ipaq_chars_in_buffer(struct tty_struct *tty); static void ipaq_destroy_lists(struct usb_serial_port *port); @@ -591,7 +593,8 @@ static spinlock_t write_list_lock; static int bytes_in; static int bytes_out; -static int ipaq_open(struct usb_serial_port *port, struct file *filp) +static int ipaq_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct ipaq_private *priv; @@ -637,10 +640,12 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp) * discipline instead of queueing. */ - port->tty->low_latency = 1; - port->tty->raw = 1; - port->tty->real_raw = 1; - + if (tty) { + tty->low_latency = 1; + /* FIXME: These two are bogus */ + tty->raw = 1; + tty->real_raw = 1; + } /* * Lose the small buffers usbserial provides. Make larger ones. */ @@ -714,7 +719,8 @@ error: } -static void ipaq_close(struct usb_serial_port *port, struct file *filp) +static void ipaq_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct ipaq_private *priv = usb_get_serial_port_data(port); @@ -751,7 +757,7 @@ static void ipaq_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -770,8 +776,8 @@ static void ipaq_read_bulk_callback(struct urb *urb) return; } -static int ipaq_write(struct usb_serial_port *port, const unsigned char *buf, - int count) +static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { const unsigned char *current_position = buf; int bytes_sent = 0; @@ -905,16 +911,18 @@ static void ipaq_write_bulk_callback(struct urb *urb) usb_serial_port_softint(port); } -static int ipaq_write_room(struct usb_serial_port *port) +static int ipaq_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ipaq_private *priv = usb_get_serial_port_data(port); dbg("%s - freelen %d", __func__, priv->free_len); return priv->free_len; } -static int ipaq_chars_in_buffer(struct usb_serial_port *port) +static int ipaq_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ipaq_private *priv = usb_get_serial_port_data(port); dbg("%s - queuelen %d", __func__, priv->queue_len); diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index bc85ca5c1c37..a89ebfe9e915 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -179,7 +179,7 @@ static void ipw_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -199,7 +199,8 @@ static void ipw_read_bulk_callback(struct urb *urb) return; } -static int ipw_open(struct usb_serial_port *port, struct file *filp) +static int ipw_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_device *dev = port->serial->dev; u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT; @@ -212,8 +213,8 @@ static int ipw_open(struct usb_serial_port *port, struct file *filp) if (!buf_flow_init) return -ENOMEM; - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* --1: Tell the modem to initialize (we think) From sniffs this is always the * first thing that gets sent to the modem during opening of the device */ @@ -301,7 +302,8 @@ static int ipw_open(struct usb_serial_port *port, struct file *filp) return 0; } -static void ipw_close(struct usb_serial_port *port, struct file * filp) +static void ipw_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct usb_device *dev = port->serial->dev; int result; @@ -384,7 +386,8 @@ static void ipw_write_bulk_callback(struct urb *urb) usb_serial_port_softint(port); } -static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int ipw_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct usb_device *dev = port->serial->dev; int ret; diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 0063c11c8081..e59155c6607d 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -85,15 +85,17 @@ static int buffer_size; /* if overridden by the user, then use the specified number of XBOFs */ static int xbof = -1; -static int ir_startup(struct usb_serial *serial); -static int ir_open(struct usb_serial_port *port, struct file *filep); -static void ir_close(struct usb_serial_port *port, struct file *filep); -static int ir_write(struct usb_serial_port *port, - const unsigned char *buf, int count); -static void ir_write_bulk_callback(struct urb *urb); -static void ir_read_bulk_callback(struct urb *urb); -static void ir_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios); +static int ir_startup (struct usb_serial *serial); +static int ir_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filep); +static void ir_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filep); +static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); +static void ir_write_bulk_callback (struct urb *urb); +static void ir_read_bulk_callback (struct urb *urb); +static void ir_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); /* Not that this lot means you can only have one per system */ static u8 ir_baud; @@ -295,7 +297,8 @@ static int ir_startup(struct usb_serial *serial) return 0; } -static int ir_open(struct usb_serial_port *port, struct file *filp) +static int ir_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { char *buffer; int result = 0; @@ -343,7 +346,8 @@ static int ir_open(struct usb_serial_port *port, struct file *filp) return result; } -static void ir_close(struct usb_serial_port *port, struct file *filp) +static void ir_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { dbg("%s - port %d", __func__, port->number); @@ -351,8 +355,8 @@ static void ir_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->read_urb); } -static int ir_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +static int ir_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { unsigned char *transfer_buffer; int result; @@ -360,11 +364,6 @@ static int ir_write(struct usb_serial_port *port, dbg("%s - port = %d, count = %d", __func__, port->number, count); - if (!port->tty) { - dev_err(&port->dev, "%s - no tty???\n", __func__); - return 0; - } - if (count == 0) return 0; @@ -450,14 +449,13 @@ static void ir_read_bulk_callback(struct urb *urb) dbg("%s - port %d", __func__, port->number); - if (!port->open_count) { + if (!port->port.count) { dbg("%s - port closed.", __func__); return; } switch (status) { case 0: /* Successful */ - /* * The first byte of the packet we get from the device * contains a busy indicator and baud rate change. @@ -465,19 +463,11 @@ static void ir_read_bulk_callback(struct urb *urb) */ if ((*data & 0x0f) > 0) ir_baud = *data & 0x0f; - - usb_serial_debug_data( - debug, - &port->dev, - __func__, - urb->actual_length, - data); - - tty = port->tty; - + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, data); + tty = port->port.tty; if (tty_buffer_request_room(tty, urb->actual_length - 1)) { - tty_insert_flip_string(tty, data + 1, - urb->actual_length - 1); + tty_insert_flip_string(tty, data+1, urb->actual_length - 1); tty_flip_buffer_push(tty); } @@ -488,11 +478,10 @@ static void ir_read_bulk_callback(struct urb *urb) */ case -EPROTO: /* taking inspiration from pl2303.c */ - - /* Continue trying to always read */ + /* Continue trying to always read */ usb_fill_bulk_urb( port->read_urb, - port->serial->dev, + port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, @@ -502,23 +491,19 @@ static void ir_read_bulk_callback(struct urb *urb) result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", + dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __func__, result); - break; - + break ; default: dbg("%s - nonzero read bulk status received: %d", - __func__, - status); - break; + __func__, status); + break ; } - return; } -static void ir_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void ir_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { unsigned char *transfer_buffer; int result; @@ -527,7 +512,7 @@ static void ir_set_termios(struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); - baud = tty_get_baud_rate(port->tty); + baud = tty_get_baud_rate(tty); /* * FIXME, we should compare the baud request against the @@ -600,8 +585,8 @@ static void ir_set_termios(struct usb_serial_port *port, __func__, result); /* Only speed changes are supported */ - tty_termios_copy_hw(port->tty->termios, old_termios); - tty_encode_baud_rate(port->tty, baud, baud); + tty_termios_copy_hw(tty->termios, old_termios); + tty_encode_baud_rate(tty, baud, baud); } static int __init ir_init(void) diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index a01e987c7d32..d65414888349 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -144,9 +144,10 @@ static void iuu_shutdown(struct usb_serial *serial) } } -static int iuu_tiocmset(struct usb_serial_port *port, struct file *file, +static int iuu_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -171,8 +172,9 @@ static int iuu_tiocmset(struct usb_serial_port *port, struct file *file, * When no card , the reader respond with TIOCM_CD * This is known as CD autodetect mechanism */ -static int iuu_tiocmget(struct usb_serial_port *port, struct file *file) +static int iuu_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; int rc; @@ -630,7 +632,7 @@ static void read_buf_callback(struct urb *urb) } dbg("%s - %i chars to write", __func__, urb->actual_length); - tty = port->tty; + tty = port->port.tty; if (data == NULL) dbg("%s - data is NULL !!!", __func__); if (tty && urb->actual_length && data) { @@ -752,11 +754,10 @@ static void iuu_uart_read_callback(struct urb *urb) /* if nothing to write call again rxcmd */ dbg("%s - rxcmd recall", __func__); iuu_led_activity_off(urb); - return; } -static int iuu_uart_write(struct usb_serial_port *port, const u8 *buf, - int count) +static int iuu_uart_write(struct tty_struct *tty, struct usb_serial_port *port, + const u8 *buf, int count) { struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -948,7 +949,8 @@ static int set_control_lines(struct usb_device *dev, u8 value) return 0; } -static void iuu_close(struct usb_serial_port *port, struct file *filp) +static void iuu_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { /* iuu_led (port,255,0,0,0); */ struct usb_serial *serial; @@ -964,8 +966,8 @@ static void iuu_close(struct usb_serial_port *port, struct file *filp) iuu_uart_off(port); if (serial->dev) { - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ priv = usb_get_serial_port_data(port); @@ -989,7 +991,8 @@ static void iuu_close(struct usb_serial_port *port, struct file *filp) } } -static int iuu_open(struct usb_serial_port *port, struct file *filp) +static int iuu_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; u8 *buf; @@ -1036,15 +1039,17 @@ static int iuu_open(struct usb_serial_port *port, struct file *filp) /* set the termios structure */ spin_lock_irqsave(&priv->lock, flags); - if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 - | TIOCM_CTS | CSTOPB | PARENB; - port->tty->termios->c_lflag = 0; - port->tty->termios->c_oflag = 0; - port->tty->termios->c_iflag = 0; + if (tty && !priv->termios_initialized) { + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 + | TIOCM_CTS | CSTOPB | PARENB; + tty->termios->c_ispeed = 9600; + tty->termios->c_ospeed = 9600; + tty->termios->c_lflag = 0; + tty->termios->c_oflag = 0; + tty->termios->c_iflag = 0; priv->termios_initialized = 1; - port->tty->low_latency = 1; + tty->low_latency = 1; priv->poll = 0; } spin_unlock_irqrestore(&priv->lock, flags); @@ -1148,7 +1153,7 @@ static int iuu_open(struct usb_serial_port *port, struct file *filp) if (result) { dev_err(&port->dev, "%s - failed submitting read urb," " error %d\n", __func__, result); - iuu_close(port, NULL); + iuu_close(tty, port, NULL); return -EPROTO; } else { dbg("%s - rxcmd OK", __func__); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 11e439b90eac..a371c41bb3ab 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -244,20 +244,9 @@ static void __exit keyspan_exit (void) module_init(keyspan_init); module_exit(keyspan_exit); -static void keyspan_rx_throttle (struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); -} - - -static void keyspan_rx_unthrottle (struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); -} - - -static void keyspan_break_ctl (struct usb_serial_port *port, int break_state) +static void keyspan_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_port_private *p_priv; dbg("%s", __func__); @@ -273,14 +262,13 @@ static void keyspan_break_ctl (struct usb_serial_port *port, int break_state) } -static void keyspan_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void keyspan_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { int baud_rate, device_port; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; unsigned int cflag; - struct tty_struct *tty = port->tty; dbg("%s", __func__); @@ -312,12 +300,11 @@ static void keyspan_set_termios (struct usb_serial_port *port, keyspan_send_setup(port, 0); } -static int keyspan_tiocmget(struct usb_serial_port *port, struct file *file) +static int keyspan_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; + struct keyspan_port_private *p_priv = usb_get_serial_port_data(port); unsigned int value; - struct keyspan_port_private *p_priv; - - p_priv = usb_get_serial_port_data(port); value = ((p_priv->rts_state) ? TIOCM_RTS : 0) | ((p_priv->dtr_state) ? TIOCM_DTR : 0) | @@ -329,18 +316,16 @@ static int keyspan_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int keyspan_tiocmset(struct usb_serial_port *port, struct file *file, +static int keyspan_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { - struct keyspan_port_private *p_priv; - - p_priv = usb_get_serial_port_data(port); + struct usb_serial_port *port = tty->driver_data; + struct keyspan_port_private *p_priv = usb_get_serial_port_data(port); if (set & TIOCM_RTS) p_priv->rts_state = 1; if (set & TIOCM_DTR) p_priv->dtr_state = 1; - if (clear & TIOCM_RTS) p_priv->rts_state = 0; if (clear & TIOCM_DTR) @@ -349,16 +334,10 @@ static int keyspan_tiocmset(struct usb_serial_port *port, struct file *file, return 0; } -static int keyspan_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; -} - - /* Write function is similar for the four protocols used - with only a minor change for usa90 (usa19hs) required */ -static int keyspan_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +/* Write function is similar for the four protocols used + with only a minor change for usa90 (usa19hs) required */ +static int keyspan_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; @@ -448,7 +427,7 @@ static void usa26_indat_callback(struct urb *urb) } port = urb->context; - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { @@ -479,7 +458,7 @@ static void usa26_indat_callback(struct urb *urb) /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) + if (port->port.count) if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { dbg("%s - resubmit read urb failed. (%d)", __func__, err); } @@ -496,7 +475,7 @@ static void usa2x_outdat_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); dbg ("%s - urb %d", __func__, urb == p_priv->out_urbs[1]); - if (port->open_count) + if (port->port.count) usb_serial_port_softint(port); } @@ -567,10 +546,10 @@ static void usa26_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -619,7 +598,7 @@ static void usa28_indat_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); data = urb->transfer_buffer; - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { for (i = 0; i < urb->actual_length ; ++i) { tty_insert_flip_char(tty, data[i], 0); @@ -629,7 +608,7 @@ static void usa28_indat_callback(struct urb *urb) /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) + if (port->port.count) if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { dbg("%s - resubmit read urb failed. (%d)", __func__, err); } @@ -704,10 +683,10 @@ static void usa28_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -797,10 +776,10 @@ static void usa49_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -839,7 +818,7 @@ static void usa49_indat_callback(struct urb *urb) } port = urb->context; - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { @@ -866,7 +845,7 @@ static void usa49_indat_callback(struct urb *urb) /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) + if (port->port.count) if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { dbg("%s - resubmit read urb failed. (%d)", __func__, err); } @@ -904,7 +883,7 @@ static void usa49wg_indat_callback(struct urb *urb) return; } port = serial->port[data[i++]]; - tty = port->tty; + tty = port->port.tty; len = data[i++]; /* 0x80 bit is error flag */ @@ -912,7 +891,7 @@ static void usa49wg_indat_callback(struct urb *urb) /* no error on any byte */ i++; for (x = 1; x < len ; ++x) - if (port->open_count) + if (port->port.count) tty_insert_flip_char(tty, data[i++], 0); else @@ -930,13 +909,13 @@ static void usa49wg_indat_callback(struct urb *urb) if (stat & RXERROR_PARITY) flag |= TTY_PARITY; /* XXX should handle break (0x10) */ - if (port->open_count) + if (port->port.count) tty_insert_flip_char(tty, data[i+1], flag); i += 2; } } - if (port->open_count) + if (port->port.count) tty_flip_buffer_push(tty); } } @@ -978,7 +957,7 @@ static void usa90_indat_callback(struct urb *urb) port = urb->context; p_priv = usb_get_serial_port_data(port); - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { /* if current mode is DMA, looks like usa28 format @@ -1021,7 +1000,7 @@ static void usa90_indat_callback(struct urb *urb) /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; - if (port->open_count) + if (port->port.count) if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) { dbg("%s - resubmit read urb failed. (%d)", __func__, err); } @@ -1064,10 +1043,10 @@ static void usa90_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -1139,10 +1118,10 @@ static void usa67_instat_callback(struct urb *urb) p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state != p_priv->dcd_state) { if (old_dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); /* else */ /* wake_up_interruptible(&p_priv->open_wait); */ } @@ -1177,8 +1156,9 @@ static void usa67_glocont_callback(struct urb *urb) } } -static int keyspan_write_room (struct usb_serial_port *port) +static int keyspan_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; int flip; @@ -1210,13 +1190,8 @@ static int keyspan_write_room (struct usb_serial_port *port) } -static int keyspan_chars_in_buffer (struct usb_serial_port *port) -{ - return 0; -} - - -static int keyspan_open (struct usb_serial_port *port, struct file *filp) +static int keyspan_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct keyspan_port_private *p_priv; struct keyspan_serial_private *s_priv; @@ -1225,7 +1200,7 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) int i, err; int baud_rate, device_port; struct urb *urb; - unsigned int cflag; + unsigned int cflag = 0; s_priv = usb_get_serial_data(serial); p_priv = usb_get_serial_port_data(port); @@ -1271,19 +1246,19 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) /* get the terminal config for the setup message now so we don't * need to send 2 of them */ - cflag = port->tty->termios->c_cflag; device_port = port->number - port->serial->minor; - - /* Baud rate calculation takes baud rate as an integer - so other rates can be generated if desired. */ - baud_rate = tty_get_baud_rate(port->tty); - /* If no match or invalid, leave as default */ - if (baud_rate >= 0 - && d_details->calculate_baud_rate(baud_rate, d_details->baudclk, - NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) { - p_priv->baud = baud_rate; + if (tty) { + cflag = tty->termios->c_cflag; + /* Baud rate calculation takes baud rate as an integer + so other rates can be generated if desired. */ + baud_rate = tty_get_baud_rate(tty); + /* If no match or invalid, leave as default */ + if (baud_rate >= 0 + && d_details->calculate_baud_rate(baud_rate, d_details->baudclk, + NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) { + p_priv->baud = baud_rate; + } } - /* set CTS/RTS handshake etc. */ p_priv->cflag = cflag; p_priv->flow_control = (cflag & CRTSCTS)? flow_cts: flow_none; @@ -1301,7 +1276,8 @@ static inline void stop_urb(struct urb *urb) usb_kill_urb(urb); } -static void keyspan_close(struct usb_serial_port *port, struct file *filp) +static void keyspan_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int i; struct usb_serial *serial = port->serial; @@ -1338,7 +1314,7 @@ static void keyspan_close(struct usb_serial_port *port, struct file *filp) stop_urb(p_priv->out_urbs[i]); } } - port->tty = NULL; + port->port.tty = NULL; } /* download the firmware to a pre-renumeration device */ @@ -2427,7 +2403,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, } /* Sending intermediate configs */ else { - if (port->open_count) + if (port->port.count) msg.portEnabled = 1; msg.txBreak = (p_priv->break_on); } diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 8bf72639b148..38b4582e0734 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -35,17 +35,18 @@ /* Function prototypes for Keyspan serial converter */ -static int keyspan_open (struct usb_serial_port *port, +static int keyspan_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); -static void keyspan_close (struct usb_serial_port *port, +static void keyspan_close (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); static int keyspan_startup (struct usb_serial *serial); static void keyspan_shutdown (struct usb_serial *serial); -static void keyspan_rx_throttle (struct usb_serial_port *port); -static void keyspan_rx_unthrottle (struct usb_serial_port *port); -static int keyspan_write_room (struct usb_serial_port *port); +static int keyspan_write_room (struct tty_struct *tty); -static int keyspan_write (struct usb_serial_port *port, +static int keyspan_write (struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); @@ -53,18 +54,14 @@ static void keyspan_send_setup (struct usb_serial_port *port, int reset_port); -static int keyspan_chars_in_buffer (struct usb_serial_port *port); -static int keyspan_ioctl (struct usb_serial_port *port, - struct file *file, - unsigned int cmd, - unsigned long arg); -static void keyspan_set_termios (struct usb_serial_port *port, +static void keyspan_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); -static void keyspan_break_ctl (struct usb_serial_port *port, +static void keyspan_break_ctl (struct tty_struct *tty, int break_state); -static int keyspan_tiocmget (struct usb_serial_port *port, +static int keyspan_tiocmget (struct tty_struct *tty, struct file *file); -static int keyspan_tiocmset (struct usb_serial_port *port, +static int keyspan_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int keyspan_fake_startup (struct usb_serial *serial); @@ -567,10 +564,6 @@ static struct usb_serial_driver keyspan_1port_device = { .close = keyspan_close, .write = keyspan_write, .write_room = keyspan_write_room, - .chars_in_buffer = keyspan_chars_in_buffer, - .throttle = keyspan_rx_throttle, - .unthrottle = keyspan_rx_unthrottle, - .ioctl = keyspan_ioctl, .set_termios = keyspan_set_termios, .break_ctl = keyspan_break_ctl, .tiocmget = keyspan_tiocmget, @@ -591,10 +584,6 @@ static struct usb_serial_driver keyspan_2port_device = { .close = keyspan_close, .write = keyspan_write, .write_room = keyspan_write_room, - .chars_in_buffer = keyspan_chars_in_buffer, - .throttle = keyspan_rx_throttle, - .unthrottle = keyspan_rx_unthrottle, - .ioctl = keyspan_ioctl, .set_termios = keyspan_set_termios, .break_ctl = keyspan_break_ctl, .tiocmget = keyspan_tiocmget, @@ -615,10 +604,6 @@ static struct usb_serial_driver keyspan_4port_device = { .close = keyspan_close, .write = keyspan_write, .write_room = keyspan_write_room, - .chars_in_buffer = keyspan_chars_in_buffer, - .throttle = keyspan_rx_throttle, - .unthrottle = keyspan_rx_unthrottle, - .ioctl = keyspan_ioctl, .set_termios = keyspan_set_termios, .break_ctl = keyspan_break_ctl, .tiocmget = keyspan_tiocmget, diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 60b3e22bd633..24a08ac2e4ee 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -171,7 +171,7 @@ static void keyspan_pda_wakeup_write(struct work_struct *work) container_of(work, struct keyspan_pda_private, wakeup_work); struct usb_serial_port *port = priv->port; - tty_wakeup(port->tty); + tty_wakeup(port->port.tty); } static void keyspan_pda_request_unthrottle(struct work_struct *work) @@ -203,7 +203,7 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work) static void keyspan_pda_rx_interrupt (struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; unsigned char *data = urb->transfer_buffer; int i; int retval; @@ -266,7 +266,7 @@ exit: } -static void keyspan_pda_rx_throttle (struct usb_serial_port *port) +static void keyspan_pda_rx_throttle(struct tty_struct *tty) { /* stop receiving characters. We just turn off the URB request, and let chars pile up in the device. If we're doing hardware @@ -274,14 +274,15 @@ static void keyspan_pda_rx_throttle (struct usb_serial_port *port) fills up. If we're doing XON/XOFF, this would be a good time to send an XOFF, although it might make sense to foist that off upon the device too. */ - + struct usb_serial_port *port = tty->driver_data; dbg("keyspan_pda_rx_throttle port %d", port->number); usb_kill_urb(port->interrupt_in_urb); } -static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port) +static void keyspan_pda_rx_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; /* just restart the receive interrupt URB */ dbg("keyspan_pda_rx_unthrottle port %d", port->number); port->interrupt_in_urb->dev = port->serial->dev; @@ -330,8 +331,9 @@ static speed_t keyspan_pda_setbaud (struct usb_serial *serial, speed_t baud) } -static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state) +static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int value; int result; @@ -354,8 +356,8 @@ static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state } -static void keyspan_pda_set_termios (struct usb_serial_port *port, - struct ktermios *old_termios) +static void keyspan_pda_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; speed_t speed; @@ -380,7 +382,7 @@ static void keyspan_pda_set_termios (struct usb_serial_port *port, For now, just do baud. */ - speed = tty_get_baud_rate(port->tty); + speed = tty_get_baud_rate(tty); speed = keyspan_pda_setbaud(serial, speed); if (speed == 0) { @@ -390,8 +392,8 @@ static void keyspan_pda_set_termios (struct usb_serial_port *port, } /* Only speed can change so copy the old h/w parameters then encode the new speed */ - tty_termios_copy_hw(port->tty->termios, old_termios); - tty_encode_baud_rate(port->tty, speed, speed); + tty_termios_copy_hw(tty->termios, old_termios); + tty_encode_baud_rate(tty, speed, speed); } @@ -425,8 +427,9 @@ static int keyspan_pda_set_modem_info(struct usb_serial *serial, return rc; } -static int keyspan_pda_tiocmget(struct usb_serial_port *port, struct file *file) +static int keyspan_pda_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int rc; unsigned char status; @@ -445,9 +448,10 @@ static int keyspan_pda_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int keyspan_pda_tiocmset(struct usb_serial_port *port, struct file *file, +static int keyspan_pda_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; int rc; unsigned char status; @@ -469,23 +473,8 @@ static int keyspan_pda_tiocmset(struct usb_serial_port *port, struct file *file, return rc; } -static int keyspan_pda_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - case TIOCGICOUNT: - /* return count of modemline transitions */ - return 0; /* TODO */ - } - - return -ENOIOCTLCMD; -} - -static int keyspan_pda_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +static int keyspan_pda_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; int request_unthrottle = 0; @@ -607,22 +596,21 @@ static void keyspan_pda_write_bulk_callback (struct urb *urb) } -static int keyspan_pda_write_room (struct usb_serial_port *port) +static int keyspan_pda_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_pda_private *priv; - priv = usb_get_serial_port_data(port); - /* used by n_tty.c for processing of tabs and such. Giving it our conservative guess is probably good enough, but needs testing by running a console through the device. */ - return (priv->tx_room); } -static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port) +static int keyspan_pda_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct keyspan_pda_private *priv; unsigned long flags; int ret = 0; @@ -640,7 +628,8 @@ static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port) } -static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp) +static int keyspan_pda_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; unsigned char room; @@ -672,7 +661,7 @@ static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp) /* the normal serial device seems to always turn on DTR and RTS here, so do the same */ - if (port->tty->termios->c_cflag & CBAUD) + if (tty && (tty->termios->c_cflag & CBAUD)) keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) ); else keyspan_pda_set_modem_info(serial, 0); @@ -690,13 +679,14 @@ error: } -static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp) +static void keyspan_pda_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; if (serial->dev) { /* the normal serial device seems to always shut off DTR and RTS now */ - if (port->tty->termios->c_cflag & HUPCL) + if (tty->termios->c_cflag & HUPCL) keyspan_pda_set_modem_info(serial, 0); /* shutdown our bulk reads and writes */ @@ -832,7 +822,6 @@ static struct usb_serial_driver keyspan_pda_device = { .chars_in_buffer = keyspan_pda_chars_in_buffer, .throttle = keyspan_pda_rx_throttle, .unthrottle = keyspan_pda_rx_unthrottle, - .ioctl = keyspan_pda_ioctl, .set_termios = keyspan_pda_set_termios, .break_ctl = keyspan_pda_break_ctl, .tiocmget = keyspan_pda_tiocmget, diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 79787eda9524..4a38ec8f5fe3 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -74,29 +74,33 @@ static int debug; */ static int klsi_105_startup (struct usb_serial *serial); static void klsi_105_shutdown (struct usb_serial *serial); -static int klsi_105_open (struct usb_serial_port *port, +static int klsi_105_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); -static void klsi_105_close (struct usb_serial_port *port, +static void klsi_105_close (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); -static int klsi_105_write (struct usb_serial_port *port, +static int klsi_105_write (struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); static void klsi_105_write_bulk_callback (struct urb *urb); -static int klsi_105_chars_in_buffer (struct usb_serial_port *port); -static int klsi_105_write_room (struct usb_serial_port *port); +static int klsi_105_chars_in_buffer (struct tty_struct *tty); +static int klsi_105_write_room (struct tty_struct *tty); static void klsi_105_read_bulk_callback (struct urb *urb); -static void klsi_105_set_termios (struct usb_serial_port *port, +static void klsi_105_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); -static void klsi_105_throttle (struct usb_serial_port *port); -static void klsi_105_unthrottle (struct usb_serial_port *port); +static void klsi_105_throttle (struct tty_struct *tty); +static void klsi_105_unthrottle (struct tty_struct *tty); /* -static void klsi_105_break_ctl (struct usb_serial_port *port, +static void klsi_105_break_ctl (struct tty_struct *tty, int break_state ); */ -static int klsi_105_tiocmget (struct usb_serial_port *port, +static int klsi_105_tiocmget (struct tty_struct *tty, struct file *file); -static int klsi_105_tiocmset (struct usb_serial_port *port, +static int klsi_105_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); @@ -361,7 +365,8 @@ static void klsi_105_shutdown (struct usb_serial *serial) } } /* klsi_105_shutdown */ -static int klsi_105_open (struct usb_serial_port *port, struct file *filp) +static int klsi_105_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int retval = 0; @@ -375,7 +380,7 @@ static int klsi_105_open (struct usb_serial_port *port, struct file *filp) /* force low_latency on so that our tty_push actually forces * the data through - * port->tty->low_latency = 1; */ + * tty->low_latency = 1; */ /* Do a defined restart: * Set up sane default baud rate and send the 'READ_ON' @@ -393,12 +398,12 @@ static int klsi_105_open (struct usb_serial_port *port, struct file *filp) /* set up termios structure */ spin_lock_irqsave (&priv->lock, flags); - priv->termios.c_iflag = port->tty->termios->c_iflag; - priv->termios.c_oflag = port->tty->termios->c_oflag; - priv->termios.c_cflag = port->tty->termios->c_cflag; - priv->termios.c_lflag = port->tty->termios->c_lflag; + priv->termios.c_iflag = tty->termios->c_iflag; + priv->termios.c_oflag = tty->termios->c_oflag; + priv->termios.c_cflag = tty->termios->c_cflag; + priv->termios.c_lflag = tty->termios->c_lflag; for (i=0; itermios.c_cc[i] = port->tty->termios->c_cc[i]; + priv->termios.c_cc[i] = tty->termios->c_cc[i]; priv->cfg.pktlen = cfg.pktlen; priv->cfg.baudrate = cfg.baudrate; priv->cfg.databits = cfg.databits; @@ -452,7 +457,8 @@ exit: } /* klsi_105_open */ -static void klsi_105_close (struct usb_serial_port *port, struct file *filp) +static void klsi_105_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int rc; @@ -493,8 +499,8 @@ static void klsi_105_close (struct usb_serial_port *port, struct file *filp) #define KLSI_105_DATA_OFFSET 2 /* in the bulk urb data block */ -static int klsi_105_write (struct usb_serial_port *port, - const unsigned char *buf, int count) +static int klsi_105_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct klsi_105_private *priv = usb_get_serial_port_data(port); int result, size; @@ -584,8 +590,9 @@ static void klsi_105_write_bulk_callback ( struct urb *urb) /* return number of characters currently in the writing process */ -static int klsi_105_chars_in_buffer (struct usb_serial_port *port) +static int klsi_105_chars_in_buffer (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int chars = 0; int i; unsigned long flags; @@ -605,8 +612,9 @@ static int klsi_105_chars_in_buffer (struct usb_serial_port *port) return (chars); } -static int klsi_105_write_room (struct usb_serial_port *port) +static int klsi_105_write_room (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; unsigned long flags; int i; int room = 0; @@ -660,7 +668,7 @@ static void klsi_105_read_bulk_callback (struct urb *urb) } else { int bytes_sent = ((__u8 *) data)[0] + ((unsigned int) ((__u8 *) data)[1] << 8); - tty = port->tty; + tty = port->port.tty; /* we should immediately resubmit the URB, before attempting * to pass the data on to the tty layer. But that needs locking * against re-entry an then mixed-up data because of @@ -699,11 +707,11 @@ static void klsi_105_read_bulk_callback (struct urb *urb) } /* klsi_105_read_bulk_callback */ -static void klsi_105_set_termios (struct usb_serial_port *port, +static void klsi_105_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct klsi_105_private *priv = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; unsigned int iflag = tty->termios->c_iflag; unsigned int old_iflag = old_termios->c_iflag; unsigned int cflag = tty->termios->c_cflag; @@ -863,8 +871,9 @@ static void klsi_105_set_termios (struct usb_serial_port *port, #if 0 -static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) +static void mct_u232_break_ctl( struct tty_struct *tty, int break_state ) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct mct_u232_private *priv = (struct mct_u232_private *)port->private; unsigned char lcr = priv->last_lcr; @@ -878,8 +887,9 @@ static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) } /* mct_u232_break_ctl */ #endif -static int klsi_105_tiocmget (struct usb_serial_port *port, struct file *file) +static int klsi_105_tiocmget (struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct klsi_105_private *priv = usb_get_serial_port_data(port); unsigned long flags; int rc; @@ -900,7 +910,7 @@ static int klsi_105_tiocmget (struct usb_serial_port *port, struct file *file) return (int)line_state; } -static int klsi_105_tiocmset (struct usb_serial_port *port, struct file *file, +static int klsi_105_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { int retval = -EINVAL; @@ -929,14 +939,16 @@ static int klsi_105_tiocmset (struct usb_serial_port *port, struct file *file, return retval; } -static void klsi_105_throttle (struct usb_serial_port *port) +static void klsi_105_throttle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->read_urb); } -static void klsi_105_unthrottle (struct usb_serial_port *port) +static void klsi_105_unthrottle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int result; dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 693f00da7c03..40c67f0096b1 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -70,19 +70,22 @@ static int debug; /* Function prototypes */ static int kobil_startup (struct usb_serial *serial); static void kobil_shutdown (struct usb_serial *serial); -static int kobil_open (struct usb_serial_port *port, struct file *filp); -static void kobil_close (struct usb_serial_port *port, struct file *filp); -static int kobil_write (struct usb_serial_port *port, +static int kobil_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void kobil_close (struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); +static int kobil_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); -static int kobil_write_room(struct usb_serial_port *port); -static int kobil_ioctl(struct usb_serial_port *port, struct file *file, +static int kobil_write_room(struct tty_struct *tty); +static int kobil_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); -static int kobil_tiocmget(struct usb_serial_port *port, struct file *file); -static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, +static int kobil_tiocmget(struct tty_struct *tty, struct file *file); +static int kobil_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static void kobil_read_int_callback( struct urb *urb ); static void kobil_write_callback( struct urb *purb ); -static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old); +static void kobil_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); static struct usb_device_id id_table [] = { @@ -201,8 +204,8 @@ static void kobil_shutdown (struct usb_serial *serial) dbg("%s - port %d", __func__, serial->port[0]->number); for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i]->open_count > 0) { - kobil_close (serial->port[i], NULL); + while (serial->port[i]->port.count > 0) { + kobil_close (NULL, serial->port[i], NULL); } kfree(usb_get_serial_port_data(serial->port[i])); usb_set_serial_port_data(serial->port[i], NULL); @@ -210,7 +213,8 @@ static void kobil_shutdown (struct usb_serial *serial) } -static int kobil_open (struct usb_serial_port *port, struct file *filp) +static int kobil_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int result = 0; struct kobil_private *priv; @@ -229,14 +233,15 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) * the data through, otherwise it is scheduled, and with high * data rates (like with OHCI) data can get lost. */ - port->tty->low_latency = 1; - - // without this, every push_tty_char is echoed :-( - port->tty->termios->c_lflag = 0; - port->tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); - port->tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; - port->tty->termios->c_oflag &= ~ONLCR; // do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) - + if (tty) { + tty->low_latency = 1; + + /* Default to echo off and other sane device settings */ + tty->termios->c_lflag = 0; + tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); + tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; + tty->termios->c_oflag &= ~ONLCR; // do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) + } // allocate memory for transfer buffer transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); if (! transfer_buffer) { @@ -330,7 +335,8 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) } -static void kobil_close (struct usb_serial_port *port, struct file *filp) +static void kobil_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); @@ -360,7 +366,7 @@ static void kobil_read_int_callback(struct urb *urb) return; } - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { // BEGIN DEBUG @@ -395,7 +401,7 @@ static void kobil_write_callback( struct urb *purb ) } -static int kobil_write (struct usb_serial_port *port, +static int kobil_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { int length = 0; @@ -417,12 +423,9 @@ static int kobil_write (struct usb_serial_port *port, // Copy data to buffer memcpy (priv->buf + priv->filled, buf, count); - usb_serial_debug_data(debug, &port->dev, __func__, count, priv->buf + priv->filled); - priv->filled = priv->filled + count; - // only send complete block. TWIN, KAAN SIM and adapter K use the same protocol. if ( ((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) || ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4))) ) { @@ -478,15 +481,17 @@ static int kobil_write (struct usb_serial_port *port, } -static int kobil_write_room (struct usb_serial_port *port) +static int kobil_write_room (struct tty_struct *tty) { //dbg("%s - port %d", __func__, port->number); + /* FIXME */ return 8; } -static int kobil_tiocmget(struct usb_serial_port *port, struct file *file) +static int kobil_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct kobil_private * priv; int result; unsigned char *transfer_buffer; @@ -524,9 +529,10 @@ static int kobil_tiocmget(struct usb_serial_port *port, struct file *file) return result; } -static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, +static int kobil_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct kobil_private * priv; int result; int dtr = 0; @@ -590,12 +596,13 @@ static int kobil_tiocmset(struct usb_serial_port *port, struct file *file, return (result < 0) ? result : 0; } -static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old) +static void kobil_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old) { struct kobil_private * priv; int result; unsigned short urb_val = 0; - int c_cflag = port->tty->termios->c_cflag; + int c_cflag = tty->termios->c_cflag; speed_t speed; void * settings; @@ -604,7 +611,7 @@ static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old // This device doesn't support ioctl calls return; - switch (speed = tty_get_baud_rate(port->tty)) { + switch (speed = tty_get_baud_rate(tty)) { case 1200: urb_val = SUSBCR_SBR_1200; break; @@ -634,8 +641,8 @@ static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old urb_val |= SUSBCR_SPASB_NoParity; strcat(settings, "No Parity"); } - port->tty->termios->c_cflag &= ~CMSPAR; - tty_encode_baud_rate(port->tty, speed, speed); + tty->termios->c_cflag &= ~CMSPAR; + tty_encode_baud_rate(tty, speed, speed); result = usb_control_msg( port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0 ), @@ -650,8 +657,9 @@ static void kobil_set_termios(struct usb_serial_port *port, struct ktermios *old kfree(settings); } -static int kobil_ioctl(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int kobil_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct kobil_private * priv = usb_get_serial_port_data(port); unsigned char *transfer_buffer; int transfer_buffer_length = 8; @@ -662,7 +670,7 @@ static int kobil_ioctl(struct usb_serial_port *port, struct file * file, unsigne return 0; switch (cmd) { - case TCFLSH: // 0x540B + case TCFLSH: transfer_buffer = kmalloc(transfer_buffer_length, GFP_KERNEL); if (! transfer_buffer) return -ENOBUFS; @@ -680,7 +688,7 @@ static int kobil_ioctl(struct usb_serial_port *port, struct file * file, unsigne dbg("%s - port %d Send reset_all_queues (FLUSH) URB returns: %i", __func__, port->number, result); kfree(transfer_buffer); - return (result < 0) ? -EFAULT : 0; + return (result < 0) ? -EIO: 0; default: return -ENOIOCTLCMD; } diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 5fc2cef30e39..7bce4302a5f9 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -92,26 +92,25 @@ static int debug; */ static int mct_u232_startup (struct usb_serial *serial); static void mct_u232_shutdown (struct usb_serial *serial); -static int mct_u232_open (struct usb_serial_port *port, +static int mct_u232_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); -static void mct_u232_close (struct usb_serial_port *port, +static void mct_u232_close (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); static void mct_u232_read_int_callback (struct urb *urb); -static void mct_u232_set_termios (struct usb_serial_port *port, +static void mct_u232_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios * old); -static int mct_u232_ioctl (struct usb_serial_port *port, - struct file * file, - unsigned int cmd, - unsigned long arg); -static void mct_u232_break_ctl (struct usb_serial_port *port, +static void mct_u232_break_ctl (struct tty_struct *tty, int break_state ); -static int mct_u232_tiocmget (struct usb_serial_port *port, +static int mct_u232_tiocmget (struct tty_struct *tty, struct file *file); -static int mct_u232_tiocmset (struct usb_serial_port *port, +static int mct_u232_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void mct_u232_throttle (struct usb_serial_port *port); -static void mct_u232_unthrottle (struct usb_serial_port *port); +static void mct_u232_throttle (struct tty_struct *tty); +static void mct_u232_unthrottle (struct tty_struct *tty); /* @@ -149,7 +148,6 @@ static struct usb_serial_driver mct_u232_device = { .throttle = mct_u232_throttle, .unthrottle = mct_u232_unthrottle, .read_int_callback = mct_u232_read_int_callback, - .ioctl = mct_u232_ioctl, .set_termios = mct_u232_set_termios, .break_ctl = mct_u232_break_ctl, .tiocmget = mct_u232_tiocmget, @@ -224,8 +222,8 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value } } -static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_port *port, - speed_t value) +static int mct_u232_set_baud_rate(struct tty_struct *tty, + struct usb_serial *serial, struct usb_serial_port *port, speed_t value) { __le32 divisor; int rc; @@ -243,7 +241,7 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_p if (rc < 0) /*FIXME: What value speed results */ err("Set BAUD RATE %d failed (error = %d)", value, rc); else - tty_encode_baud_rate(port->tty, speed, speed); + tty_encode_baud_rate(tty, speed, speed); dbg("set_baud_rate: value: 0x%x, divisor: 0x%x", value, divisor); /* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which @@ -272,7 +270,7 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_p err("Sending USB device request code %d failed (error = %d)", MCT_U232_SET_UNKNOWN1_REQUEST, rc); - if (port && C_CRTSCTS(port->tty)) { + if (port && C_CRTSCTS(tty)) { cts_enable_byte = 1; } @@ -411,7 +409,8 @@ static void mct_u232_shutdown (struct usb_serial *serial) } } /* mct_u232_shutdown */ -static int mct_u232_open (struct usb_serial_port *port, struct file *filp) +static int mct_u232_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); @@ -437,7 +436,7 @@ static int mct_u232_open (struct usb_serial_port *port, struct file *filp) * either. */ spin_lock_irqsave(&priv->lock, flags); - if (port->tty->termios->c_cflag & CBAUD) + if (tty && (tty->termios->c_cflag & CBAUD)) priv->control_state = TIOCM_DTR | TIOCM_RTS; else priv->control_state = 0; @@ -481,15 +480,16 @@ error: } /* mct_u232_open */ -static void mct_u232_close (struct usb_serial_port *port, struct file *filp) +static void mct_u232_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { unsigned int c_cflag; unsigned int control_state; struct mct_u232_private *priv = usb_get_serial_port_data(port); dbg("%s port %d", __func__, port->number); - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; mutex_lock(&port->serial->disc_mutex); if (c_cflag & HUPCL && !port->serial->disconnected) { /* drop DTR and RTS */ @@ -553,7 +553,7 @@ static void mct_u232_read_int_callback (struct urb *urb) */ if (urb->transfer_buffer_length > 2) { int i; - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { for (i = 0; i < urb->actual_length ; ++i) { tty_insert_flip_char(tty, data[i], 0); @@ -583,7 +583,7 @@ static void mct_u232_read_int_callback (struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & MCT_U232_LSR_ERR) { - tty = port->tty; + tty = port->port.tty; /* Overrun Error */ if (priv->last_lsr & MCT_U232_LSR_OE) { } @@ -606,12 +606,13 @@ exit: __func__, retval); } /* mct_u232_read_int_callback */ -static void mct_u232_set_termios (struct usb_serial_port *port, +static void mct_u232_set_termios (struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); - struct ktermios *termios = port->tty->termios; + struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned long flags; @@ -638,7 +639,7 @@ static void mct_u232_set_termios (struct usb_serial_port *port, mct_u232_set_modem_ctrl(serial, control_state); } - mct_u232_set_baud_rate(serial, port, tty_get_baud_rate(port->tty)); + mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty)); if ((cflag & CBAUD) == B0 ) { dbg("%s: baud is B0", __func__); @@ -689,8 +690,9 @@ static void mct_u232_set_termios (struct usb_serial_port *port, spin_unlock_irqrestore(&priv->lock, flags); } /* mct_u232_set_termios */ -static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) +static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned char lcr; @@ -709,8 +711,9 @@ static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) } /* mct_u232_break_ctl */ -static int mct_u232_tiocmget (struct usb_serial_port *port, struct file *file) +static int mct_u232_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned int control_state; unsigned long flags; @@ -724,9 +727,10 @@ static int mct_u232_tiocmget (struct usb_serial_port *port, struct file *file) return control_state; } -static int mct_u232_tiocmset (struct usb_serial_port *port, struct file *file, +static int mct_u232_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned int control_state; @@ -751,73 +755,46 @@ static int mct_u232_tiocmset (struct usb_serial_port *port, struct file *file, return mct_u232_set_modem_ctrl(serial, control_state); } -static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file, - unsigned int cmd, unsigned long arg) -{ - dbg("%scmd=0x%x", __func__, cmd); - - /* Based on code from acm.c and others */ - switch (cmd) { - case TIOCMIWAIT: - /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ - /* TODO */ - return( 0 ); - - case TIOCGICOUNT: - /* return count of modemline transitions */ - /* TODO */ - return 0; - - default: - dbg("%s: arg not supported - 0x%04x", __func__,cmd); - return(-ENOIOCTLCMD); - break; - } - return 0; -} /* mct_u232_ioctl */ - -static void mct_u232_throttle (struct usb_serial_port *port) +static void mct_u232_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int control_state; - struct tty_struct *tty; - tty = port->tty; dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); priv->rx_flags |= THROTTLED; if (C_CRTSCTS(tty)) { - priv->control_state &= ~TIOCM_RTS; - control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); - (void) mct_u232_set_modem_ctrl(port->serial, control_state); + priv->control_state &= ~TIOCM_RTS; + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + (void) mct_u232_set_modem_ctrl(port->serial, control_state); } else { - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } } -static void mct_u232_unthrottle (struct usb_serial_port *port) +static void mct_u232_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int control_state; - struct tty_struct *tty; dbg("%s - port %d", __func__, port->number); - tty = port->tty; spin_lock_irqsave(&priv->lock, flags); if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) { - priv->rx_flags &= ~THROTTLED; - priv->control_state |= TIOCM_RTS; - control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); - (void) mct_u232_set_modem_ctrl(port->serial, control_state); + priv->rx_flags &= ~THROTTLED; + priv->control_state |= TIOCM_RTS; + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + (void) mct_u232_set_modem_ctrl(port->serial, control_state); } else { - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } } diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 50f1fe263338..d47f0814ce2d 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -218,7 +218,7 @@ static void mos7720_bulk_in_callback(struct urb *urb) data = urb->transfer_buffer; - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -264,7 +264,7 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) dbg("Entering ........."); - tty = mos7720_port->port->tty; + tty = mos7720_port->port->port.tty; if (tty && mos7720_port->open) tty_wakeup(tty); @@ -320,7 +320,8 @@ static int send_mos_cmd(struct usb_serial *serial, __u8 request, __u16 value, return status; } -static int mos7720_open(struct usb_serial_port *port, struct file * filp) +static int mos7720_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct usb_serial *serial; struct usb_serial_port *port0; @@ -443,14 +444,12 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) data = 0x0c; send_mos_cmd(serial, MOS_WRITE, port_number, 0x01, &data); -//Matrix - /* force low_latency on so that our tty_push actually forces * * the data through,otherwise it is scheduled, and with * * high data rates (like with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* see if we've set up our endpoint info yet * * (can't set it up in mos7720_startup as the * @@ -515,8 +514,9 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) * system, * Otherwise we return a negative error number. */ -static int mos7720_chars_in_buffer(struct usb_serial_port *port) +static int mos7720_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int i; int chars = 0; struct moschip_port *mos7720_port; @@ -537,7 +537,8 @@ static int mos7720_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void mos7720_close(struct usb_serial_port *port, struct file *filp) +static void mos7720_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial; struct moschip_port *mos7720_port; @@ -588,8 +589,9 @@ static void mos7720_close(struct usb_serial_port *port, struct file *filp) dbg("Leaving %s", __func__); } -static void mos7720_break(struct usb_serial_port *port, int break_state) +static void mos7720_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; unsigned char data; struct usb_serial *serial; struct moschip_port *mos7720_port; @@ -621,8 +623,9 @@ static void mos7720_break(struct usb_serial_port *port, int break_state) * If successful, we return the amount of room that we have for this port * Otherwise we return a negative error number. */ -static int mos7720_write_room(struct usb_serial_port *port) +static int mos7720_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port; int room = 0; int i; @@ -645,8 +648,8 @@ static int mos7720_write_room(struct usb_serial_port *port) return room; } -static int mos7720_write(struct usb_serial_port *port, - const unsigned char *data, int count) +static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { int status; int i; @@ -719,10 +722,10 @@ exit: return bytes_sent; } -static void mos7720_throttle(struct usb_serial_port *port) +static void mos7720_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port; - struct tty_struct *tty; int status; dbg("%s- port %d\n", __func__, port->number); @@ -739,16 +742,10 @@ static void mos7720_throttle(struct usb_serial_port *port) dbg("%s: Entering ..........", __func__); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = mos7720_write(port, &stop_char, 1); + status = mos7720_write(tty, port, &stop_char, 1); if (status <= 0) return; } @@ -764,11 +761,11 @@ static void mos7720_throttle(struct usb_serial_port *port) } } -static void mos7720_unthrottle(struct usb_serial_port *port) +static void mos7720_unthrottle(struct tty_struct *tty) { - struct tty_struct *tty; - int status; + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port = usb_get_serial_port_data(port); + int status; if (mos7720_port == NULL) return; @@ -780,16 +777,10 @@ static void mos7720_unthrottle(struct usb_serial_port *port) dbg("%s: Entering ..........", __func__); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = mos7720_write(port, &start_char, 1); + status = mos7720_write(tty, port, &start_char, 1); if (status <= 0) return; } @@ -1011,12 +1002,12 @@ static int send_cmd_write_baud_rate(struct moschip_port *mos7720_port, * This routine is called to set the UART on the device to match * the specified new settings. */ -static void change_port_settings(struct moschip_port *mos7720_port, +static void change_port_settings(struct tty_struct *tty, + struct moschip_port *mos7720_port, struct ktermios *old_termios) { struct usb_serial_port *port; struct usb_serial *serial; - struct tty_struct *tty; int baud; unsigned cflag; unsigned iflag; @@ -1042,8 +1033,6 @@ static void change_port_settings(struct moschip_port *mos7720_port, return; } - tty = mos7720_port->port->tty; - dbg("%s: Entering ..........", __func__); lData = UART_LCR_WLEN8; @@ -1198,14 +1187,13 @@ static void change_port_settings(struct moschip_port *mos7720_port, * this function is called by the tty driver when it wants to change the * termios structure. */ -static void mos7720_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void mos7720_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { int status; unsigned int cflag; struct usb_serial *serial; struct moschip_port *mos7720_port; - struct tty_struct *tty; serial = port->serial; @@ -1214,9 +1202,6 @@ static void mos7720_set_termios(struct usb_serial_port *port, if (mos7720_port == NULL) return; - tty = port->tty; - - if (!mos7720_port->open) { dbg("%s - port not opened", __func__); return; @@ -1237,7 +1222,7 @@ static void mos7720_set_termios(struct usb_serial_port *port, dbg("%s - port %d", __func__, port->number); /* change the port settings to the new ones specified */ - change_port_settings(mos7720_port, old_termios); + change_port_settings(tty, mos7720_port, old_termios); if(!port->read_urb) { dbg("%s","URB KILLED !!!!!\n"); @@ -1264,13 +1249,13 @@ static void mos7720_set_termios(struct usb_serial_port *port, * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */ -static int get_lsr_info(struct moschip_port *mos7720_port, +static int get_lsr_info(struct tty_struct *tty, struct moschip_port *mos7720_port, unsigned int __user *value) { int count; unsigned int result = 0; - count = mos7720_chars_in_buffer(mos7720_port->port); + count = mos7720_chars_in_buffer(tty); if (count == 0) { dbg("%s -- Empty", __func__); result = TIOCSER_TEMT; @@ -1290,7 +1275,7 @@ static int get_number_bytes_avail(struct moschip_port *mos7720_port, unsigned int __user *value) { unsigned int result = 0; - struct tty_struct *tty = mos7720_port->port->tty; + struct tty_struct *tty = mos7720_port->port->port.tty; if (!tty) return -ENOIOCTLCMD; @@ -1407,9 +1392,10 @@ static int get_serial_info(struct moschip_port *mos7720_port, return 0; } -static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, +static int mos7720_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7720_port; struct async_icount cnow; struct async_icount cprev; @@ -1431,9 +1417,10 @@ static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); - return get_lsr_info(mos7720_port, (unsigned int __user *)arg); + return get_lsr_info(tty, mos7720_port, (unsigned int __user *)arg); return 0; + /* FIXME: These should be using the mode methods */ case TIOCMBIS: case TIOCMBIC: case TIOCMSET: @@ -1452,10 +1439,6 @@ static int mos7720_ioctl(struct usb_serial_port *port, struct file *file, return get_serial_info(mos7720_port, (struct serial_struct __user *)arg); - case TIOCSSERIAL: - dbg("%s (%d) TIOCSSERIAL", __func__, port->number); - break; - case TIOCMIWAIT: dbg("%s (%d) TIOCMIWAIT", __func__, port->number); cprev = mos7720_port->icount; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 78f2f6db494d..2b1fded6619d 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -710,7 +710,7 @@ static void mos7840_bulk_in_callback(struct urb *urb) dbg("%s", "Entering ........... \n"); if (urb->actual_length) { - tty = mos7840_port->port->tty; + tty = mos7840_port->port->port.tty; if (tty) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -774,7 +774,7 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) dbg("%s \n", "Entering ........."); - tty = mos7840_port->port->tty; + tty = mos7840_port->port->port.tty; if (tty && mos7840_port->open) tty_wakeup(tty); @@ -804,7 +804,8 @@ static int mos7840_serial_probe(struct usb_serial *serial, * Otherwise we return a negative error number. *****************************************************************************/ -static int mos7840_open(struct usb_serial_port *port, struct file *filp) +static int mos7840_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int response; int j; @@ -1008,8 +1009,8 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) * the data through,otherwise it is scheduled, and with * * high data rates (like with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* Check to see if we've set up our endpoint info yet * * (can't set it up in mos7840_startup as the structures * * were not set up at that time.) */ @@ -1104,11 +1105,12 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) * been written, but hasn't made it out the port yet) * If successful, we return the number of bytes left to be written in the * system, - * Otherwise we return a negative error number. + * Otherwise we return zero. *****************************************************************************/ -static int mos7840_chars_in_buffer(struct usb_serial_port *port) +static int mos7840_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int i; int chars = 0; unsigned long flags; @@ -1118,21 +1120,19 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port) if (mos7840_port_paranoia_check(port, __func__)) { dbg("%s", "Invalid port \n"); - return -1; + return 0; } mos7840_port = mos7840_get_port_private(port); if (mos7840_port == NULL) { dbg("%s \n", "mos7840_break:leaving ..........."); - return -1; + return 0; } spin_lock_irqsave(&mos7840_port->pool_lock,flags); - for (i = 0; i < NUM_URBS; ++i) { - if (mos7840_port->busy[i]) { + for (i = 0; i < NUM_URBS; ++i) + if (mos7840_port->busy[i]) chars += URB_TRANSFER_BUFFER_SIZE; - } - } spin_unlock_irqrestore(&mos7840_port->pool_lock,flags); dbg("%s - returns %d", __func__, chars); return chars; @@ -1149,7 +1149,8 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port) * 3. A timeout of 3 seconds without activity has expired * ************************************************************************/ -static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) +static void mos7840_block_until_tx_empty(struct tty_struct *tty, + struct moschip_port *mos7840_port) { int timeout = HZ / 10; int wait = 30; @@ -1157,7 +1158,7 @@ static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) while (1) { - count = mos7840_chars_in_buffer(mos7840_port->port); + count = mos7840_chars_in_buffer(tty); /* Check for Buffer status */ if (count <= 0) { @@ -1185,7 +1186,8 @@ static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port) * this function is called by the tty driver when a port is closed *****************************************************************************/ -static void mos7840_close(struct usb_serial_port *port, struct file *filp) +static void mos7840_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial; struct moschip_port *mos7840_port; @@ -1226,20 +1228,17 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) } } - if (serial->dev) { + if (serial->dev) /* flush and block until tx is empty */ - mos7840_block_until_tx_empty(mos7840_port); - } + mos7840_block_until_tx_empty(tty, mos7840_port); /* While closing port, shutdown all bulk read, write * * and interrupt read if they exists */ if (serial->dev) { - if (mos7840_port->write_urb) { dbg("%s", "Shutdown bulk write\n"); usb_kill_urb(mos7840_port->write_urb); } - if (mos7840_port->read_urb) { dbg("%s", "Shutdown bulk read\n"); usb_kill_urb(mos7840_port->read_urb); @@ -1247,11 +1246,10 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) if ((&mos7840_port->control_urb)) { dbg("%s", "Shutdown control read\n"); // usb_kill_urb (mos7840_port->control_urb); - } } -// if(mos7840_port->ctrl_buf != NULL) -// kfree(mos7840_port->ctrl_buf); +// if(mos7840_port->ctrl_buf != NULL) +// kfree(mos7840_port->ctrl_buf); port0->open_ports--; dbg("mos7840_num_open_ports in close%d:in port%d\n", port0->open_ports, port->number); @@ -1293,15 +1291,15 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) * ************************************************************************/ -static void mos7840_block_until_chase_response(struct moschip_port - *mos7840_port) +static void mos7840_block_until_chase_response(struct tty_struct *tty, + struct moschip_port *mos7840_port) { int timeout = 1 * HZ; int wait = 10; int count; while (1) { - count = mos7840_chars_in_buffer(mos7840_port->port); + count = mos7840_chars_in_buffer(tty); /* Check for Buffer status */ if (count <= 0) { @@ -1328,8 +1326,9 @@ static void mos7840_block_until_chase_response(struct moschip_port * mos7840_break * this function sends a break to the port *****************************************************************************/ -static void mos7840_break(struct usb_serial_port *port, int break_state) +static void mos7840_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; unsigned char data; struct usb_serial *serial; struct moschip_port *mos7840_port; @@ -1354,17 +1353,14 @@ static void mos7840_break(struct usb_serial_port *port, int break_state) return; } - if (serial->dev) { - + if (serial->dev) /* flush and block until tx is empty */ - mos7840_block_until_chase_response(mos7840_port); - } + mos7840_block_until_chase_response(tty, mos7840_port); - if (break_state == -1) { + if (break_state == -1) data = mos7840_port->shadowLCR | LCR_SET_BREAK; - } else { + else data = mos7840_port->shadowLCR & ~LCR_SET_BREAK; - } mos7840_port->shadowLCR = data; dbg("mcs7840_break mos7840_port->shadowLCR is %x\n", @@ -1383,8 +1379,9 @@ static void mos7840_break(struct usb_serial_port *port, int break_state) * Otherwise we return a negative error number. *****************************************************************************/ -static int mos7840_write_room(struct usb_serial_port *port) +static int mos7840_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int i; int room = 0; unsigned long flags; @@ -1426,7 +1423,7 @@ static int mos7840_write_room(struct usb_serial_port *port) * return a negative error number. *****************************************************************************/ -static int mos7840_write(struct usb_serial_port *port, +static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *data, int count) { int status; @@ -1555,8 +1552,7 @@ static int mos7840_write(struct usb_serial_port *port, mos7840_port->icount.tx += transfer_size; smp_wmb(); dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx); - exit: - +exit: return bytes_sent; } @@ -1567,10 +1563,10 @@ static int mos7840_write(struct usb_serial_port *port, * being read from the port. *****************************************************************************/ -static void mos7840_throttle(struct usb_serial_port *port) +static void mos7840_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; - struct tty_struct *tty; int status; if (mos7840_port_paranoia_check(port, __func__)) { @@ -1592,32 +1588,21 @@ static void mos7840_throttle(struct usb_serial_port *port) dbg("%s", "Entering .......... \n"); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the stop character */ if (I_IXOFF(tty)) { unsigned char stop_char = STOP_CHAR(tty); - status = mos7840_write(port, &stop_char, 1); - if (status <= 0) { + status = mos7840_write(tty, port, &stop_char, 1); + if (status <= 0) return; - } } - /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { mos7840_port->shadowMCR &= ~MCR_RTS; status = 0; - status = - mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mos7840_port->shadowMCR); - - if (status < 0) { + if (status < 0) return; - } } return; @@ -1628,9 +1613,9 @@ static void mos7840_throttle(struct usb_serial_port *port) * this function is called by the tty driver when it wants to resume the data * being read from the port (called after SerialThrottle is called) *****************************************************************************/ -static void mos7840_unthrottle(struct usb_serial_port *port) +static void mos7840_unthrottle(struct tty_struct *tty) { - struct tty_struct *tty; + struct usb_serial_port *port = tty->driver_data; int status; struct moschip_port *mos7840_port = mos7840_get_port_private(port); @@ -1649,38 +1634,28 @@ static void mos7840_unthrottle(struct usb_serial_port *port) dbg("%s", "Entering .......... \n"); - tty = port->tty; - if (!tty) { - dbg("%s - no tty available", __func__); - return; - } - /* if we are implementing XON/XOFF, send the start character */ if (I_IXOFF(tty)) { unsigned char start_char = START_CHAR(tty); - status = mos7840_write(port, &start_char, 1); - if (status <= 0) { + status = mos7840_write(tty, port, &start_char, 1); + if (status <= 0) return; - } } /* if we are implementing RTS/CTS, toggle that line */ if (tty->termios->c_cflag & CRTSCTS) { mos7840_port->shadowMCR |= MCR_RTS; status = 0; - status = - mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, + status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mos7840_port->shadowMCR); - if (status < 0) { + if (status < 0) return; - } } - - return; } -static int mos7840_tiocmget(struct usb_serial_port *port, struct file *file) +static int mos7840_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; unsigned int result; __u16 msr; @@ -1708,9 +1683,10 @@ static int mos7840_tiocmget(struct usb_serial_port *port, struct file *file) return result; } -static int mos7840_tiocmset(struct usb_serial_port *port, struct file *file, +static int mos7840_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port; unsigned int mcr; int status; @@ -1949,10 +1925,9 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port, * the specified new settings. *****************************************************************************/ -static void mos7840_change_port_settings(struct moschip_port *mos7840_port, - struct ktermios *old_termios) +static void mos7840_change_port_settings(struct tty_struct *tty, + struct moschip_port *mos7840_port, struct ktermios *old_termios) { - struct tty_struct *tty; int baud; unsigned cflag; unsigned iflag; @@ -1988,8 +1963,6 @@ static void mos7840_change_port_settings(struct moschip_port *mos7840_port, return; } - tty = mos7840_port->port->tty; - dbg("%s", "Entering .......... \n"); lData = LCR_BITS_8; @@ -2131,14 +2104,14 @@ static void mos7840_change_port_settings(struct moschip_port *mos7840_port, * the termios structure *****************************************************************************/ -static void mos7840_set_termios(struct usb_serial_port *port, +static void mos7840_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { int status; unsigned int cflag; struct usb_serial *serial; struct moschip_port *mos7840_port; - struct tty_struct *tty; dbg("mos7840_set_termios: START\n"); if (mos7840_port_paranoia_check(port, __func__)) { dbg("%s", "Invalid port \n"); @@ -2157,8 +2130,6 @@ static void mos7840_set_termios(struct usb_serial_port *port, if (mos7840_port == NULL) return; - tty = port->tty; - if (!mos7840_port->open) { dbg("%s - port not opened", __func__); return; @@ -2176,7 +2147,7 @@ static void mos7840_set_termios(struct usb_serial_port *port, /* change the port settings to the new ones specified */ - mos7840_change_port_settings(mos7840_port, old_termios); + mos7840_change_port_settings(tty, mos7840_port, old_termios); if (!mos7840_port->read_urb) { dbg("%s", "URB KILLED !!!!!\n"); @@ -2205,13 +2176,13 @@ static void mos7840_set_termios(struct usb_serial_port *port, * allows an RS485 driver to be written in user space. *****************************************************************************/ -static int mos7840_get_lsr_info(struct moschip_port *mos7840_port, +static int mos7840_get_lsr_info(struct tty_struct *tty, unsigned int __user *value) { int count; unsigned int result = 0; - count = mos7840_chars_in_buffer(mos7840_port->port); + count = mos7840_chars_in_buffer(tty); if (count == 0) { dbg("%s -- Empty", __func__); result = TIOCSER_TEMT; @@ -2227,6 +2198,8 @@ static int mos7840_get_lsr_info(struct moschip_port *mos7840_port, * function to set modem info *****************************************************************************/ +/* FIXME: Should be using the model control hooks */ + static int mos7840_set_modem_info(struct moschip_port *mos7840_port, unsigned int cmd, unsigned int __user *value) { @@ -2304,9 +2277,8 @@ static int mos7840_get_modem_info(struct moschip_port *mos7840_port, __u16 msr; unsigned int mcr = mos7840_port->shadowMCR; int status = 0; - status = - mos7840_get_uart_reg(mos7840_port->port, MODEM_STATUS_REGISTER, - &msr); + status = mos7840_get_uart_reg(mos7840_port->port, + MODEM_STATUS_REGISTER, &msr); result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) /* 0x002 */ |((mcr & MCR_RTS) ? TIOCM_RTS : 0) /* 0x004 */ |((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0) /* 0x020 */ @@ -2359,12 +2331,12 @@ static int mos7840_get_serial_info(struct moschip_port *mos7840_port, * this function handles any ioctl calls to the driver *****************************************************************************/ -static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, +static int mos7840_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; void __user *argp = (void __user *)arg; struct moschip_port *mos7840_port; - struct tty_struct *tty; struct async_icount cnow; struct async_icount cprev; @@ -2381,8 +2353,6 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, if (mos7840_port == NULL) return -1; - tty = mos7840_port->port->tty; - dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); switch (cmd) { @@ -2390,9 +2360,10 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); - return mos7840_get_lsr_info(mos7840_port, argp); + return mos7840_get_lsr_info(tty, argp); return 0; + /* FIXME: use the modem hooks and remove this */ case TIOCMBIS: case TIOCMBIC: case TIOCMSET: @@ -2463,13 +2434,9 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, if (copy_to_user(argp, &icount, sizeof(icount))) return -EFAULT; return 0; - - case TIOCEXBAUD: - return 0; default: break; } - return -ENOIOCTLCMD; } diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index 43c8894353bf..d6736531a0fa 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -64,7 +64,7 @@ static void navman_read_int_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -79,7 +79,8 @@ exit: __func__, result); } -static int navman_open(struct usb_serial_port *port, struct file *filp) +static int navman_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int result = 0; @@ -96,14 +97,15 @@ static int navman_open(struct usb_serial_port *port, struct file *filp) return result; } -static void navman_close(struct usb_serial_port *port, struct file *filp) +static void navman_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->interrupt_in_urb); } -static int navman_write(struct usb_serial_port *port, +static int navman_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { dbg("%s - port %d", __func__, port->number); diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 7b7422f49478..5a2d045562f0 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -61,12 +61,12 @@ static int debug; #define BT_IGNITIONPRO_ID 0x2000 /* This one seems to be a re-branded ZyXEL device */ /* function prototypes */ -static int omninet_open (struct usb_serial_port *port, struct file *filp); -static void omninet_close (struct usb_serial_port *port, struct file *filp); +static int omninet_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void omninet_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static void omninet_read_bulk_callback (struct urb *urb); static void omninet_write_bulk_callback (struct urb *urb); -static int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int omninet_write_room (struct usb_serial_port *port); +static int omninet_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); +static int omninet_write_room (struct tty_struct *tty); static void omninet_shutdown (struct usb_serial *serial); static int omninet_attach (struct usb_serial *serial); @@ -157,7 +157,8 @@ static int omninet_attach (struct usb_serial *serial) return 0; } -static int omninet_open (struct usb_serial_port *port, struct file *filp) +static int omninet_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct usb_serial_port *wport; @@ -166,7 +167,7 @@ static int omninet_open (struct usb_serial_port *port, struct file *filp) dbg("%s - port %d", __func__, port->number); wport = serial->port[1]; - wport->tty = port->tty; + wport->port.tty = tty; /* FIXME */ /* Start reading from the device */ usb_fill_bulk_urb(port->read_urb, serial->dev, @@ -181,7 +182,8 @@ static int omninet_open (struct usb_serial_port *port, struct file *filp) return result; } -static void omninet_close (struct usb_serial_port *port, struct file * filp) +static void omninet_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { dbg("%s - port %d", __func__, port->number); usb_kill_urb(port->read_urb); @@ -221,9 +223,9 @@ static void omninet_read_bulk_callback (struct urb *urb) if (urb->actual_length && header->oh_len) { for (i = 0; i < header->oh_len; i++) { - tty_insert_flip_char(port->tty, data[OMNINET_DATAOFFSET + i], 0); + tty_insert_flip_char(port->port.tty, data[OMNINET_DATAOFFSET + i], 0); } - tty_flip_buffer_push(port->tty); + tty_flip_buffer_push(port->port.tty); } /* Continue trying to always read */ @@ -238,7 +240,8 @@ static void omninet_read_bulk_callback (struct urb *urb) return; } -static int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct usb_serial_port *wport = serial->port[1]; @@ -290,8 +293,9 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf } -static int omninet_write_room (struct usb_serial_port *port) +static int omninet_write_room (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; struct usb_serial_port *wport = serial->port[1]; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 1e936a1cbe0b..4350990abf14 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -43,29 +43,23 @@ #include /* Function prototypes */ -static int option_open(struct usb_serial_port *port, struct file *filp); -static void option_close(struct usb_serial_port *port, struct file *filp); +static int option_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void option_close(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); static int option_startup(struct usb_serial *serial); static void option_shutdown(struct usb_serial *serial); -static void option_rx_throttle(struct usb_serial_port *port); -static void option_rx_unthrottle(struct usb_serial_port *port); -static int option_write_room(struct usb_serial_port *port); +static int option_write_room(struct tty_struct *tty); static void option_instat_callback(struct urb *urb); -static int option_write(struct usb_serial_port *port, +static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); - -static int option_chars_in_buffer(struct usb_serial_port *port); -static int option_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg); -static void option_set_termios(struct usb_serial_port *port, - struct ktermios *old); -static void option_break_ctl(struct usb_serial_port *port, int break_state); -static int option_tiocmget(struct usb_serial_port *port, struct file *file); -static int option_tiocmset(struct usb_serial_port *port, struct file *file, +static int option_chars_in_buffer(struct tty_struct *tty); +static void option_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static int option_tiocmget(struct tty_struct *tty, struct file *file); +static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static int option_send_setup(struct usb_serial_port *port); +static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *port); /* Vendor and product IDs */ #define OPTION_VENDOR_ID 0x0AF0 @@ -342,11 +336,7 @@ static struct usb_serial_driver option_1port_device = { .write = option_write, .write_room = option_write_room, .chars_in_buffer = option_chars_in_buffer, - .throttle = option_rx_throttle, - .unthrottle = option_rx_unthrottle, - .ioctl = option_ioctl, .set_termios = option_set_termios, - .break_ctl = option_break_ctl, .tiocmget = option_tiocmget, .tiocmset = option_tiocmset, .attach = option_startup, @@ -417,33 +407,18 @@ static void __exit option_exit(void) module_init(option_init); module_exit(option_exit); -static void option_rx_throttle(struct usb_serial_port *port) -{ - dbg("%s", __func__); -} - -static void option_rx_unthrottle(struct usb_serial_port *port) -{ - dbg("%s", __func__); -} - -static void option_break_ctl(struct usb_serial_port *port, int break_state) -{ - /* Unfortunately, I don't know how to send a break */ - dbg("%s", __func__); -} - -static void option_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void option_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { dbg("%s", __func__); /* Doesn't support option setting */ - tty_termios_copy_hw(port->tty->termios, old_termios); - option_send_setup(port); + tty_termios_copy_hw(tty->termios, old_termios); + option_send_setup(tty, port); } -static int option_tiocmget(struct usb_serial_port *port, struct file *file) +static int option_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; unsigned int value; struct option_port_private *portdata; @@ -459,9 +434,10 @@ static int option_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int option_tiocmset(struct usb_serial_port *port, struct file *file, +static int option_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; portdata = usb_get_serial_port_data(port); @@ -476,17 +452,11 @@ static int option_tiocmset(struct usb_serial_port *port, struct file *file, portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; - return option_send_setup(port); -} - -static int option_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; + return option_send_setup(tty, port); } /* Write */ -static int option_write(struct usb_serial_port *port, +static int option_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct option_port_private *portdata; @@ -562,7 +532,7 @@ static void option_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -572,7 +542,7 @@ static void option_indat_callback(struct urb *urb) } /* Resubmit urb so we continue receiving */ - if (port->open_count && status != -ESHUTDOWN) { + if (port->port.count && status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err) printk(KERN_ERR "%s: resubmit read urb failed. " @@ -638,9 +608,9 @@ static void option_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) && + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state && !portdata->dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType,req_pkt->bRequest); @@ -658,8 +628,9 @@ static void option_instat_callback(struct urb *urb) } } -static int option_write_room(struct usb_serial_port *port) +static int option_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; int i; int data_len = 0; @@ -678,8 +649,9 @@ static int option_write_room(struct usb_serial_port *port) return data_len; } -static int option_chars_in_buffer(struct usb_serial_port *port) +static int option_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct option_port_private *portdata; int i; int data_len = 0; @@ -698,7 +670,8 @@ static int option_chars_in_buffer(struct usb_serial_port *port) return data_len; } -static int option_open(struct usb_serial_port *port, struct file *filp) +static int option_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct option_port_private *portdata; struct usb_serial *serial = port->serial; @@ -748,14 +721,16 @@ static int option_open(struct usb_serial_port *port, struct file *filp) usb_pipeout(urb->pipe), 0); */ } - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; - option_send_setup(port); + option_send_setup(tty, port); return (0); } -static void option_close(struct usb_serial_port *port, struct file *filp) +static void option_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int i; struct usb_serial *serial = port->serial; @@ -770,7 +745,7 @@ static void option_close(struct usb_serial_port *port, struct file *filp) if (serial->dev) { mutex_lock(&serial->disc_mutex); if (!serial->disconnected) - option_send_setup(port); + option_send_setup(tty, port); mutex_unlock(&serial->disc_mutex); /* Stop reading/writing urbs */ @@ -779,7 +754,7 @@ static void option_close(struct usb_serial_port *port, struct file *filp) for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); } - port->tty = NULL; + port->port.tty = NULL; /* FIXME */ } /* Helper functions used by option_setup_urbs */ @@ -841,7 +816,8 @@ static void option_setup_urbs(struct usb_serial *serial) * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN * CDC. */ -static int option_send_setup(struct usb_serial_port *port) +static int option_send_setup(struct tty_struct *tty, + struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct option_port_private *portdata; @@ -850,7 +826,7 @@ static int option_send_setup(struct usb_serial_port *port) portdata = usb_get_serial_port_data(port); - if (port->tty) { + if (tty) { int val = 0; if (portdata->dtr_state) val |= 0x01; @@ -861,7 +837,6 @@ static int option_send_setup(struct usb_serial_port *port) usb_rcvctrlpipe(serial->dev, 0), 0x22,0x21,val,ifNum,NULL,0,USB_CTRL_SET_TIMEOUT); } - return 0; } diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index a9625c180dc3..069d276a5276 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -140,22 +140,23 @@ struct oti6858_control_pkt { && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt) ) /* function prototypes */ -static int oti6858_open(struct usb_serial_port *port, struct file *filp); -static void oti6858_close(struct usb_serial_port *port, struct file *filp); -static void oti6858_set_termios(struct usb_serial_port *port, - struct ktermios *old); -static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, +static int oti6858_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void oti6858_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +static void oti6858_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); +static int oti6858_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void oti6858_read_int_callback(struct urb *urb); static void oti6858_read_bulk_callback(struct urb *urb); static void oti6858_write_bulk_callback(struct urb *urb); -static int oti6858_write(struct usb_serial_port *port, +static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); -static int oti6858_write_room(struct usb_serial_port *port); -static void oti6858_break_ctl(struct usb_serial_port *port, int break_state); -static int oti6858_chars_in_buffer(struct usb_serial_port *port); -static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file); -static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, +static int oti6858_write_room(struct tty_struct *tty); +static int oti6858_chars_in_buffer(struct tty_struct *tty); +static int oti6858_tiocmget(struct tty_struct *tty, struct file *file); +static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static int oti6858_startup(struct usb_serial *serial); static void oti6858_shutdown(struct usb_serial *serial); @@ -184,7 +185,6 @@ static struct usb_serial_driver oti6858_device = { .close = oti6858_close, .write = oti6858_write, .ioctl = oti6858_ioctl, - .break_ctl = oti6858_break_ctl, .set_termios = oti6858_set_termios, .tiocmget = oti6858_tiocmget, .tiocmset = oti6858_tiocmset, @@ -395,7 +395,7 @@ static int oti6858_startup(struct usb_serial *serial) return -ENOMEM; } -static int oti6858_write(struct usb_serial_port *port, +static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct oti6858_private *priv = usb_get_serial_port_data(port); @@ -413,8 +413,9 @@ static int oti6858_write(struct usb_serial_port *port, return count; } -static int oti6858_write_room(struct usb_serial_port *port) +static int oti6858_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -428,8 +429,9 @@ static int oti6858_write_room(struct usb_serial_port *port) return room; } -static int oti6858_chars_in_buffer(struct usb_serial_port *port) +static int oti6858_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -443,8 +445,8 @@ static int oti6858_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void oti6858_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void oti6858_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -455,22 +457,22 @@ static void oti6858_set_termios(struct usb_serial_port *port, dbg("%s(port = %d)", __func__, port->number); - if (!port->tty || !port->tty->termios) { + if (!tty) { dbg("%s(): no tty structures", __func__); return; } spin_lock_irqsave(&priv->lock, flags); if (!priv->flags.termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 38400; + tty->termios->c_ospeed = 38400; priv->flags.termios_initialized = 1; - port->tty->termios->c_ispeed = 38400; - port->tty->termios->c_ospeed = 38400; } spin_unlock_irqrestore(&priv->lock, flags); - cflag = port->tty->termios->c_cflag; + cflag = tty->termios->c_cflag; spin_lock_irqsave(&priv->lock, flags); divisor = priv->pending_setup.divisor; @@ -500,7 +502,7 @@ static void oti6858_set_termios(struct usb_serial_port *port, * guarantee that any other baud rate will work (especially * the higher ones) */ - br = tty_get_baud_rate(port->tty); + br = tty_get_baud_rate(tty); if (br == 0) { divisor = 0; } else { @@ -511,7 +513,7 @@ static void oti6858_set_termios(struct usb_serial_port *port, new_divisor = (96000000 + 8 * br) / (16 * br); real_br = 96000000 / (16 * new_divisor); divisor = cpu_to_le16(new_divisor); - tty_encode_baud_rate(port->tty, real_br, real_br); + tty_encode_baud_rate(tty, real_br, real_br); } frame_fmt &= ~FMT_STOP_BITS_MASK; @@ -564,7 +566,8 @@ static void oti6858_set_termios(struct usb_serial_port *port, spin_unlock_irqrestore(&priv->lock, flags); } -static int oti6858_open(struct usb_serial_port *port, struct file *filp) +static int oti6858_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct oti6858_private *priv = usb_get_serial_port_data(port); struct ktermios tmp_termios; @@ -578,7 +581,7 @@ static int oti6858_open(struct usb_serial_port *port, struct file *filp) usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); - if (port->open_count != 1) + if (port->port.count != 1) return 0; if ((buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) { @@ -617,18 +620,19 @@ static int oti6858_open(struct usb_serial_port *port, struct file *filp) if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); - oti6858_close(port, NULL); + oti6858_close(tty, port, NULL); return -EPROTO; } /* setup termios */ - if (port->tty) - oti6858_set_termios(port, &tmp_termios); + if (tty) + oti6858_set_termios(tty, port, &tmp_termios); return 0; } -static void oti6858_close(struct usb_serial_port *port, struct file *filp) +static void oti6858_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -641,7 +645,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); timeout = 30 * HZ; /* PL2303_CLOSING_WAIT */ init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); dbg("%s(): entering wait loop", __func__); for (;;) { set_current_state(TASK_INTERRUPTIBLE); @@ -654,7 +658,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); dbg("%s(): after wait loop", __func__); /* clear out any remaining data in the buffer */ @@ -669,7 +673,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) /* data is in the buffer to compute a delay */ /* that is not unnecessarily long) */ /* FIXME - bps = tty_get_baud_rate(port->tty); + bps = tty_get_baud_rate(tty); if (bps > 1200) timeout = max((HZ*2560)/bps,HZ/10); else @@ -690,7 +694,7 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->interrupt_in_urb); /* - if (port->tty && (port->tty->termios->c_cflag) & HUPCL) { + if (tty && (tty->termios->c_cflag) & HUPCL) { // drop DTR and RTS spin_lock_irqsave(&priv->lock, flags); priv->pending_setup.control &= ~CONTROL_MASK; @@ -699,9 +703,10 @@ static void oti6858_close(struct usb_serial_port *port, struct file *filp) */ } -static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, +static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; @@ -732,8 +737,9 @@ static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, return 0; } -static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file) +static int oti6858_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned pin_state; @@ -802,26 +808,15 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, +static int oti6858_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - void __user *user_arg = (void __user *) arg; - unsigned int x; + struct usb_serial_port *port = tty->driver_data; dbg("%s(port = %d, cmd = 0x%04x, arg = 0x%08lx)", __func__, port->number, cmd, arg); switch (cmd) { - case TIOCMBIS: - if (copy_from_user(&x, user_arg, sizeof(x))) - return -EFAULT; - return oti6858_tiocmset(port, NULL, x, 0); - - case TIOCMBIC: - if (copy_from_user(&x, user_arg, sizeof(x))) - return -EFAULT; - return oti6858_tiocmset(port, NULL, 0, x); - case TIOCMIWAIT: dbg("%s(): TIOCMIWAIT", __func__); return wait_modem_info(port, arg); @@ -834,24 +829,6 @@ static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static void oti6858_break_ctl(struct usb_serial_port *port, int break_state) -{ - int state; - - dbg("%s(port = %d)", __func__, port->number); - - state = (break_state == 0) ? 0 : 1; - dbg("%s(): turning break %s", __func__, state ? "on" : "off"); - - /* FIXME */ -/* - result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), - BREAK_REQUEST, BREAK_REQUEST_TYPE, state, - 0, NULL, 0, 100); - if (result != 0) - dbg("%s(): error sending break", __func__); - */ -} static void oti6858_shutdown(struct usb_serial *serial) { @@ -1002,7 +979,7 @@ static void oti6858_read_bulk_callback(struct urb *urb) spin_unlock_irqrestore(&priv->lock, flags); if (status != 0) { - if (!port->open_count) { + if (!port->port.count) { dbg("%s(): port is closed, exiting", __func__); return; } @@ -1020,14 +997,14 @@ static void oti6858_read_bulk_callback(struct urb *urb) return; } - tty = port->tty; + tty = port->port.tty; if (tty != NULL && urb->actual_length > 0) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } // schedule the interrupt urb if we are still open */ - if (port->open_count != 0) { + if (port->port.count != 0) { port->interrupt_in_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result != 0) { diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 2a0dd1b50dc4..a0016725d314 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -458,8 +458,8 @@ static void pl2303_send(struct usb_serial_port *port) usb_serial_port_softint(port); } -static int pl2303_write(struct usb_serial_port *port, const unsigned char *buf, - int count) +static int pl2303_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -478,8 +478,9 @@ static int pl2303_write(struct usb_serial_port *port, const unsigned char *buf, return count; } -static int pl2303_write_room(struct usb_serial_port *port) +static int pl2303_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -494,8 +495,9 @@ static int pl2303_write_room(struct usb_serial_port *port) return room; } -static int pl2303_chars_in_buffer(struct usb_serial_port *port) +static int pl2303_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -510,8 +512,8 @@ static int pl2303_chars_in_buffer(struct usb_serial_port *port) return chars; } -static void pl2303_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void pl2303_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct pl2303_private *priv = usb_get_serial_port_data(port); @@ -526,11 +528,10 @@ static void pl2303_set_termios(struct usb_serial_port *port, spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B9600 | CS8 | CREAD | - HUPCL | CLOCAL; - port->tty->termios->c_ispeed = 9600; - port->tty->termios->c_ospeed = 9600; + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 9600; + tty->termios->c_ospeed = 9600; priv->termios_initialized = 1; } spin_unlock_irqrestore(&priv->lock, flags); @@ -539,16 +540,16 @@ static void pl2303_set_termios(struct usb_serial_port *port, serial settings even to the same values as before. Thus we actually need to filter in this specific case */ - if (!tty_termios_hw_change(port->tty->termios, old_termios)) + if (!tty_termios_hw_change(tty->termios, old_termios)) return; - cflag = port->tty->termios->c_cflag; + cflag = tty->termios->c_cflag; buf = kzalloc(7, GFP_KERNEL); if (!buf) { dev_err(&port->dev, "%s - out of memory.\n", __func__); /* Report back no change occurred */ - *port->tty->termios = *old_termios; + *tty->termios = *old_termios; return; } @@ -569,7 +570,7 @@ static void pl2303_set_termios(struct usb_serial_port *port, dbg("%s - data bits = %d", __func__, buf[6]); } - baud = tty_get_baud_rate(port->tty);; + baud = tty_get_baud_rate(tty); dbg("%s - baud = %d", __func__, baud); if (baud) { buf[0] = baud & 0xff; @@ -646,12 +647,13 @@ static void pl2303_set_termios(struct usb_serial_port *port, /* FIXME: Need to read back resulting baud rate */ if (baud) - tty_encode_baud_rate(port->tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); kfree(buf); } -static void pl2303_close(struct usb_serial_port *port, struct file *filp) +static void pl2303_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -666,7 +668,7 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); timeout = PL2303_CLOSING_WAIT; init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (pl2303_buf_data_avail(priv->buf) == 0 || @@ -678,7 +680,7 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); /* clear out any remaining data in the buffer */ pl2303_buf_clear(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); @@ -690,7 +692,7 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) /* for lower rates we should really know how much */ /* data is in the buffer to compute a delay */ /* that is not unnecessarily long) */ - bps = tty_get_baud_rate(port->tty); + bps = tty_get_baud_rate(tty); if (bps > 1200) timeout = max((HZ*2560)/bps,HZ/10); else @@ -703,8 +705,8 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->read_urb); usb_kill_urb(port->interrupt_in_urb); - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ spin_lock_irqsave(&priv->lock, flags); @@ -715,7 +717,8 @@ static void pl2303_close(struct usb_serial_port *port, struct file *filp) } } -static int pl2303_open(struct usb_serial_port *port, struct file *filp) +static int pl2303_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -734,9 +737,8 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) } /* Setup termios */ - if (port->tty) { - pl2303_set_termios(port, &tmp_termios); - } + if (tty) + pl2303_set_termios(tty, port, &tmp_termios); //FIXME: need to assert RTS and DTR if CRTSCTS off @@ -746,7 +748,7 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) if (result) { dev_err(&port->dev, "%s - failed submitting read urb," " error %d\n", __func__, result); - pl2303_close(port, NULL); + pl2303_close(tty, port, NULL); return -EPROTO; } @@ -756,15 +758,16 @@ static int pl2303_open(struct usb_serial_port *port, struct file *filp) if (result) { dev_err(&port->dev, "%s - failed submitting interrupt urb," " error %d\n", __func__, result); - pl2303_close(port, NULL); + pl2303_close(tty, port, NULL); return -EPROTO; } return 0; } -static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file, +static int pl2303_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; @@ -787,8 +790,9 @@ static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file, return set_control_lines(port->serial->dev, control); } -static int pl2303_tiocmget(struct usb_serial_port *port, struct file *file) +static int pl2303_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int mcr; @@ -853,9 +857,10 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) return 0; } -static int pl2303_ioctl(struct usb_serial_port *port, struct file *file, +static int pl2303_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd); switch (cmd) { @@ -871,8 +876,9 @@ static int pl2303_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static void pl2303_break_ctl(struct usb_serial_port *port, int break_state) +static void pl2303_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; u16 state; int result; @@ -1001,7 +1007,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) if (status) { dbg("%s - urb status = %d", __func__, status); - if (!port->open_count) { + if (!port->port.count) { dbg("%s - port is closed, exiting.", __func__); return; } @@ -1044,7 +1050,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dbg("%s - tty_flag = %d", __func__, tty_flag); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -1056,7 +1062,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) } /* Schedule the next read _if_ we are still open */ - if (port->open_count) { + if (port->port.count) { urb->dev = port->serial->dev; result = usb_submit_urb(urb, GFP_ATOMIC); if (result) diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 94bddf06ea4f..f823e4dcea1e 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -229,8 +229,8 @@ static void safe_read_bulk_callback (struct urb *urb) int actual_length = data[length - 2] >> 2; if (actual_length <= (length - 2)) { info ("%s - actual: %d", __func__, actual_length); - tty_insert_flip_string(port->tty, data, actual_length); - tty_flip_buffer_push (port->tty); + tty_insert_flip_string(port->port.tty, data, actual_length); + tty_flip_buffer_push (port->port.tty); } else { err ("%s - inconsistent lengths %d:%d", __func__, actual_length, length); @@ -239,8 +239,8 @@ static void safe_read_bulk_callback (struct urb *urb) err ("%s - bad CRC %x", __func__, fcs); } } else { - tty_insert_flip_string(port->tty, data, length); - tty_flip_buffer_push (port->tty); + tty_insert_flip_string(port->port.tty, data, length); + tty_flip_buffer_push (port->port.tty); } /* Continue trying to always read */ @@ -255,7 +255,8 @@ static void safe_read_bulk_callback (struct urb *urb) } } -static int safe_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int safe_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { unsigned char *data; int result; @@ -349,8 +350,9 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i return (count); } -static int safe_write_room (struct usb_serial_port *port) +static int safe_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; int room = 0; /* Default: no room */ unsigned long flags; diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 29074c1ba22b..892020d48555 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -250,7 +250,8 @@ struct sierra_port_private { int ri_state; }; -static int sierra_send_setup(struct usb_serial_port *port) +static int sierra_send_setup(struct tty_struct *tty, + struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct sierra_port_private *portdata; @@ -260,7 +261,7 @@ static int sierra_send_setup(struct usb_serial_port *port) portdata = usb_get_serial_port_data(port); - if (port->tty) { + if (tty) { int val = 0; if (portdata->dtr_state) val |= 0x01; @@ -284,32 +285,17 @@ static int sierra_send_setup(struct usb_serial_port *port) return 0; } -static void sierra_rx_throttle(struct usb_serial_port *port) +static void sierra_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { dbg("%s", __func__); + tty_termios_copy_hw(tty->termios, old_termios); + sierra_send_setup(tty, port); } -static void sierra_rx_unthrottle(struct usb_serial_port *port) -{ - dbg("%s", __func__); -} - -static void sierra_break_ctl(struct usb_serial_port *port, int break_state) -{ - /* Unfortunately, I don't know how to send a break */ - dbg("%s", __func__); -} - -static void sierra_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) -{ - dbg("%s", __func__); - tty_termios_copy_hw(port->tty->termios, old_termios); - sierra_send_setup(port); -} - -static int sierra_tiocmget(struct usb_serial_port *port, struct file *file) +static int sierra_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; unsigned int value; struct sierra_port_private *portdata; @@ -325,9 +311,10 @@ static int sierra_tiocmget(struct usb_serial_port *port, struct file *file) return value; } -static int sierra_tiocmset(struct usb_serial_port *port, struct file *file, +static int sierra_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct sierra_port_private *portdata; portdata = usb_get_serial_port_data(port); @@ -341,13 +328,7 @@ static int sierra_tiocmset(struct usb_serial_port *port, struct file *file, portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; - return sierra_send_setup(port); -} - -static int sierra_ioctl(struct usb_serial_port *port, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; + return sierra_send_setup(tty, port); } static void sierra_outdat_callback(struct urb *urb) @@ -374,8 +355,8 @@ static void sierra_outdat_callback(struct urb *urb) } /* Write */ -static int sierra_write(struct usb_serial_port *port, - const unsigned char *buf, int count) +static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct sierra_port_private *portdata = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -463,7 +444,7 @@ static void sierra_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->tty; + tty = port->port.tty; if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); @@ -473,7 +454,7 @@ static void sierra_indat_callback(struct urb *urb) } /* Resubmit urb so we continue receiving */ - if (port->open_count && status != -ESHUTDOWN) { + if (port->port.count && status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err) dev_err(&port->dev, "resubmit read urb failed." @@ -517,9 +498,9 @@ static void sierra_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->tty && !C_CLOCAL(port->tty) && + if (port->port.tty && !C_CLOCAL(port->port.tty) && old_dcd_state && !portdata->dcd_state) - tty_hangup(port->tty); + tty_hangup(port->port.tty); } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType, req_pkt->bRequest); @@ -537,8 +518,9 @@ static void sierra_instat_callback(struct urb *urb) } } -static int sierra_write_room(struct usb_serial_port *port) +static int sierra_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct sierra_port_private *portdata = usb_get_serial_port_data(port); unsigned long flags; @@ -557,22 +539,8 @@ static int sierra_write_room(struct usb_serial_port *port) return 2048; } -static int sierra_chars_in_buffer(struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); - - /* - * We can't really account for how much data we - * have sent out, but hasn't made it through to the - * device as we can't see the backend here, so just - * tell the tty layer that everything is flushed. - * - * FIXME: should walk the outstanding urbs info - */ - return 0; -} - -static int sierra_open(struct usb_serial_port *port, struct file *filp) +static int sierra_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct sierra_port_private *portdata; struct usb_serial *serial = port->serial; @@ -612,9 +580,10 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) } } - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; - sierra_send_setup(port); + sierra_send_setup(tty, port); /* start up the interrupt endpoint if we have one */ if (port->interrupt_in_urb) { @@ -626,7 +595,8 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) return 0; } -static void sierra_close(struct usb_serial_port *port, struct file *filp) +static void sierra_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int i; struct usb_serial *serial = port->serial; @@ -641,7 +611,7 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) if (serial->dev) { mutex_lock(&serial->disc_mutex); if (!serial->disconnected) - sierra_send_setup(port); + sierra_send_setup(tty, port); mutex_unlock(&serial->disc_mutex); /* Stop reading/writing urbs */ @@ -651,7 +621,7 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) usb_kill_urb(port->interrupt_in_urb); - port->tty = NULL; + port->port.tty = NULL; /* FIXME */ } static int sierra_startup(struct usb_serial *serial) @@ -754,12 +724,7 @@ static struct usb_serial_driver sierra_device = { .close = sierra_close, .write = sierra_write, .write_room = sierra_write_room, - .chars_in_buffer = sierra_chars_in_buffer, - .throttle = sierra_rx_throttle, - .unthrottle = sierra_rx_unthrottle, - .ioctl = sierra_ioctl, .set_termios = sierra_set_termios, - .break_ctl = sierra_break_ctl, .tiocmget = sierra_tiocmget, .tiocmset = sierra_tiocmset, .attach = sierra_startup, diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 55b2570b8b8b..58495f5cca1f 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -448,7 +448,8 @@ static void spcp8x5_set_workMode(struct usb_device *dev, u16 value, /* close the serial port. We should wait for data sending to device 1st and * then kill all urb. */ -static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) +static void spcp8x5_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -464,7 +465,7 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); timeout = SPCP8x5_CLOSING_WAIT; init_waitqueue_entry(&wait, current); - add_wait_queue(&port->tty->write_wait, &wait); + add_wait_queue(&tty->write_wait, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (ringbuf_avail_data(priv->buf) == 0 || @@ -475,7 +476,7 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) spin_lock_irqsave(&priv->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->tty->write_wait, &wait); + remove_wait_queue(&tty->write_wait, &wait); /* clear out any remaining data in the buffer */ clear_ringbuf(priv->buf); @@ -486,7 +487,7 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) * flow control for data rates of 1200 bps or more, for lower rates we * should really know how much data is in the buffer to compute a delay * that is not unnecessarily long) */ - bps = tty_get_baud_rate(port->tty); + bps = tty_get_baud_rate(tty); if (bps > 1200) timeout = max((HZ*2560) / bps, HZ/10); else @@ -495,8 +496,8 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) schedule_timeout(timeout); /* clear control lines */ - if (port->tty) { - c_cflag = port->tty->termios->c_cflag; + if (tty) { + c_cflag = tty->termios->c_cflag; if (c_cflag & HUPCL) { spin_lock_irqsave(&priv->lock, flags); priv->line_control = 0; @@ -518,14 +519,14 @@ static void spcp8x5_close(struct usb_serial_port *port, struct file *filp) } /* set the serial param for transfer. we should check if we really need to - * transfer. then if be set flow contorl we should do this too. */ -static void spcp8x5_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) + * transfer. if we set flow control we should do this too. */ +static void spcp8x5_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; - unsigned int cflag = port->tty->termios->c_cflag; + unsigned int cflag = tty->termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned short uartdata; unsigned char buf[2] = {0, 0}; @@ -533,21 +534,19 @@ static void spcp8x5_set_termios(struct usb_serial_port *port, int i; u8 control; - if ((!port->tty) || (!port->tty->termios)) - return; - /* for the 1st time call this function */ spin_lock_irqsave(&priv->lock, flags); if (!priv->termios_initialized) { - *(port->tty->termios) = tty_std_termios; - port->tty->termios->c_cflag = B115200 | CS8 | CREAD | - HUPCL | CLOCAL; + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 115200; + tty->termios->c_ospeed = 115200; priv->termios_initialized = 1; } spin_unlock_irqrestore(&priv->lock, flags); /* check that they really want us to change something */ - if (!tty_termios_hw_change(port->tty->termios, old_termios)) + if (!tty_termios_hw_change(tty->termios, old_termios)) return; /* set DTR/RTS active */ @@ -567,7 +566,7 @@ static void spcp8x5_set_termios(struct usb_serial_port *port, } /* Set Baud Rate */ - baud = tty_get_baud_rate(port->tty);; + baud = tty_get_baud_rate(tty);; switch (baud) { case 300: buf[0] = 0x00; break; case 600: buf[0] = 0x01; break; @@ -643,7 +642,8 @@ static void spcp8x5_set_termios(struct usb_serial_port *port, /* open the serial port. do some usb system call. set termios and get the line * status of the device. then submit the read urb */ -static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) +static int spcp8x5_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { struct ktermios tmp_termios; struct usb_serial *serial = port->serial; @@ -665,7 +665,7 @@ static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) return ret; spin_lock_irqsave(&priv->lock, flags); - if (port->tty->termios->c_cflag & CBAUD) + if (tty && (tty->termios->c_cflag & CBAUD)) priv->line_control = MCR_DTR | MCR_RTS; else priv->line_control = 0; @@ -674,8 +674,8 @@ static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) spcp8x5_set_ctrlLine(serial->dev, priv->line_control , priv->type); /* Setup termios */ - if (port->tty) - spcp8x5_set_termios(port, &tmp_termios); + if (tty) + spcp8x5_set_termios(tty, port, &tmp_termios); spcp8x5_get_msr(serial->dev, &status, priv->type); @@ -690,7 +690,7 @@ static int spcp8x5_open(struct usb_serial_port *port, struct file *filp) port->read_urb->dev = serial->dev; ret = usb_submit_urb(port->read_urb, GFP_KERNEL); if (ret) { - spcp8x5_close(port, NULL); + spcp8x5_close(tty, port, NULL); return -EPROTO; } return 0; @@ -717,7 +717,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) /* check the urb status */ if (urb->status) { - if (!port->open_count) + if (!port->port.count) return; if (urb->status == -EPROTO) { /* spcp8x5 mysteriously fails with -EPROTO */ @@ -755,7 +755,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -767,7 +767,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) } /* Schedule the next read _if_ we are still open */ - if (port->open_count) { + if (port->port.count) { urb->dev = port->serial->dev; result = usb_submit_urb(urb , GFP_ATOMIC); if (result) @@ -866,7 +866,7 @@ static void spcp8x5_write_bulk_callback(struct urb *urb) } /* write data to ring buffer. and then start the write transfer */ -static int spcp8x5_write(struct usb_serial_port *port, +static int spcp8x5_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); @@ -925,9 +925,10 @@ static int spcp8x5_wait_modem_info(struct usb_serial_port *port, return 0; } -static int spcp8x5_ioctl(struct usb_serial_port *port, struct file *file, +static int spcp8x5_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd); switch (cmd) { @@ -943,9 +944,10 @@ static int spcp8x5_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } -static int spcp8x5_tiocmset(struct usb_serial_port *port, struct file *file, +static int spcp8x5_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 control; @@ -965,8 +967,9 @@ static int spcp8x5_tiocmset(struct usb_serial_port *port, struct file *file, return spcp8x5_set_ctrlLine(port->serial->dev, control , priv->type); } -static int spcp8x5_tiocmget(struct usb_serial_port *port, struct file *file) +static int spcp8x5_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); unsigned long flags; unsigned int mcr; @@ -989,8 +992,9 @@ static int spcp8x5_tiocmget(struct usb_serial_port *port, struct file *file) } /* get the avail space room in ring buffer */ -static int spcp8x5_write_room(struct usb_serial_port *port) +static int spcp8x5_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -1003,8 +1007,9 @@ static int spcp8x5_write_room(struct usb_serial_port *port) } /* get the number of avail data in write ring buffer */ -static int spcp8x5_chars_in_buffer(struct usb_serial_port *port) +static int spcp8x5_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct spcp8x5_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index a26a629dfc4f..48831a755fc1 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -70,6 +70,7 @@ #include #include +#include #include #include #include @@ -149,21 +150,23 @@ struct ti_device { static int ti_startup(struct usb_serial *serial); static void ti_shutdown(struct usb_serial *serial); -static int ti_open(struct usb_serial_port *port, struct file *file); -static void ti_close(struct usb_serial_port *port, struct file *file); -static int ti_write(struct usb_serial_port *port, const unsigned char *data, - int count); -static int ti_write_room(struct usb_serial_port *port); -static int ti_chars_in_buffer(struct usb_serial_port *port); -static void ti_throttle(struct usb_serial_port *port); -static void ti_unthrottle(struct usb_serial_port *port); -static int ti_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg); -static void ti_set_termios(struct usb_serial_port *port, +static int ti_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *file); +static void ti_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *file); +static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count); +static int ti_write_room(struct tty_struct *tty); +static int ti_chars_in_buffer(struct tty_struct *tty); +static void ti_throttle(struct tty_struct *tty); +static void ti_unthrottle(struct tty_struct *tty); +static int ti_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static void ti_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); -static int ti_tiocmget(struct usb_serial_port *port, struct file *file); -static int ti_tiocmset(struct usb_serial_port *port, struct file *file, +static int ti_tiocmget(struct tty_struct *tty, struct file *file); +static int ti_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void ti_break(struct usb_serial_port *port, int break_state); +static void ti_break(struct tty_struct *tty, int break_state); static void ti_interrupt_callback(struct urb *urb); static void ti_bulk_in_callback(struct urb *urb); static void ti_bulk_out_callback(struct urb *urb); @@ -192,8 +195,7 @@ static int ti_command_in_sync(struct ti_device *tdev, __u8 command, static int ti_write_byte(struct ti_device *tdev, unsigned long addr, __u8 mask, __u8 byte); -static int ti_download_firmware(struct ti_device *tdev, char *fw_name); - +static int ti_download_firmware(struct ti_device *tdev, int type); /* circular buffer */ static struct circ_buf *ti_buf_alloc(void); @@ -430,11 +432,10 @@ static int ti_startup(struct usb_serial *serial) /* if we have only 1 configuration, download firmware */ if (dev->descriptor.bNumConfigurations == 1) { - if (tdev->td_is_3410) - status = ti_download_firmware(tdev, "ti_3410.fw"); + status = ti_download_firmware(tdev, 3410); else - status = ti_download_firmware(tdev, "ti_5052.fw"); + status = ti_download_firmware(tdev, 5052); if (status) goto free_tdev; @@ -519,7 +520,8 @@ static void ti_shutdown(struct usb_serial *serial) } -static int ti_open(struct usb_serial_port *port, struct file *file) +static int ti_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *file) { struct ti_port *tport = usb_get_serial_port_data(port); struct ti_device *tdev; @@ -543,9 +545,9 @@ static int ti_open(struct usb_serial_port *port, struct file *file) if (mutex_lock_interruptible(&tdev->td_open_close_lock)) return -ERESTARTSYS; - if (port->tty) - port->tty->low_latency = - (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; + if (tty) + tty->low_latency = + (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; port_number = port->number - port->serial->minor; @@ -573,7 +575,8 @@ static int ti_open(struct usb_serial_port *port, struct file *file) } } - ti_set_termios(port, port->tty->termios); + if (tty) + ti_set_termios(tty, port, tty->termios); dbg("%s - sending TI_OPEN_PORT", __func__); status = ti_command_out_sync(tdev, TI_OPEN_PORT, @@ -610,7 +613,8 @@ static int ti_open(struct usb_serial_port *port, struct file *file) usb_clear_halt(dev, port->write_urb->pipe); usb_clear_halt(dev, port->read_urb->pipe); - ti_set_termios(port, port->tty->termios); + if (tty) + ti_set_termios(tty, port, tty->termios); dbg("%s - sending TI_OPEN_PORT (2)", __func__); status = ti_command_out_sync(tdev, TI_OPEN_PORT, @@ -661,7 +665,8 @@ release_lock: } -static void ti_close(struct usb_serial_port *port, struct file *file) +static void ti_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *file) { struct ti_device *tdev; struct ti_port *tport; @@ -707,8 +712,8 @@ static void ti_close(struct usb_serial_port *port, struct file *file) } -static int ti_write(struct usb_serial_port *port, const unsigned char *data, - int count) +static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *data, int count) { struct ti_port *tport = usb_get_serial_port_data(port); unsigned long flags; @@ -733,8 +738,9 @@ static int ti_write(struct usb_serial_port *port, const unsigned char *data, } -static int ti_write_room(struct usb_serial_port *port) +static int ti_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); int room = 0; unsigned long flags; @@ -753,8 +759,9 @@ static int ti_write_room(struct usb_serial_port *port) } -static int ti_chars_in_buffer(struct usb_serial_port *port) +static int ti_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; @@ -773,32 +780,26 @@ static int ti_chars_in_buffer(struct usb_serial_port *port) } -static void ti_throttle(struct usb_serial_port *port) +static void ti_throttle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); - struct tty_struct *tty; dbg("%s - port %d", __func__, port->number); if (tport == NULL) return; - tty = port->tty; - if (!tty) { - dbg("%s - no tty", __func__); - return; - } - if (I_IXOFF(tty) || C_CRTSCTS(tty)) ti_stop_read(tport, tty); } -static void ti_unthrottle(struct usb_serial_port *port) +static void ti_unthrottle(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); - struct tty_struct *tty; int status; dbg("%s - port %d", __func__, port->number); @@ -806,12 +807,6 @@ static void ti_unthrottle(struct usb_serial_port *port) if (tport == NULL) return; - tty = port->tty; - if (!tty) { - dbg("%s - no tty", __func__); - return; - } - if (I_IXOFF(tty) || C_CRTSCTS(tty)) { status = ti_restart_read(tport, tty); if (status) @@ -820,9 +815,10 @@ static void ti_unthrottle(struct usb_serial_port *port) } -static int ti_ioctl(struct usb_serial_port *port, struct file *file, +static int ti_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); struct async_icount cnow; struct async_icount cprev; @@ -875,11 +871,10 @@ static int ti_ioctl(struct usb_serial_port *port, struct file *file, } -static void ti_set_termios(struct usb_serial_port *port, - struct ktermios *old_termios) +static void ti_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) { struct ti_port *tport = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; struct ti_uart_config *config; tcflag_t cflag,iflag; int baud; @@ -1008,8 +1003,9 @@ static void ti_set_termios(struct usb_serial_port *port, } -static int ti_tiocmget(struct usb_serial_port *port, struct file *file) +static int ti_tiocmget(struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); unsigned int result; unsigned int msr; @@ -1040,9 +1036,10 @@ static int ti_tiocmget(struct usb_serial_port *port, struct file *file) } -static int ti_tiocmset(struct usb_serial_port *port, struct file *file, +static int ti_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); unsigned int mcr; unsigned long flags; @@ -1074,8 +1071,9 @@ static int ti_tiocmset(struct usb_serial_port *port, struct file *file, } -static void ti_break(struct usb_serial_port *port, int break_state) +static void ti_break(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); int status; @@ -1213,14 +1211,14 @@ static void ti_bulk_in_callback(struct urb *urb) return; } - if (port->tty && urb->actual_length) { + if (port->port.tty && urb->actual_length) { usb_serial_debug_data(debug, dev, __func__, urb->actual_length, urb->transfer_buffer); if (!tport->tp_is_open) dbg("%s - port closed, dropping data", __func__); else - ti_recv(&urb->dev->dev, port->tty, urb->transfer_buffer, + ti_recv(&urb->dev->dev, port->port.tty, urb->transfer_buffer, urb->actual_length); spin_lock(&tport->tp_lock); @@ -1302,7 +1300,7 @@ static void ti_send(struct ti_port *tport) { int count, result; struct usb_serial_port *port = tport->tp_port; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; /* FIXME */ unsigned long flags; @@ -1442,8 +1440,9 @@ static int ti_set_serial_info(struct ti_port *tport, return -EFAULT; tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS; - if (port->tty) - port->tty->low_latency = + /* FIXME */ + if (port->port.tty) + port->port.tty->low_latency = (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; tport->tp_closing_wait = new_serial.closing_wait; @@ -1477,7 +1476,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) tport->tp_msr = msr & TI_MSR_MASK; /* handle CTS flow control */ - tty = tport->tp_port->tty; + tty = tport->tp_port->port.tty; if (tty && C_CRTSCTS(tty)) { if (msr & TI_MSR_CTS) { tty->hw_stopped = 0; @@ -1655,65 +1654,65 @@ static int ti_write_byte(struct ti_device *tdev, unsigned long addr, return status; } - -static int ti_download_firmware(struct ti_device *tdev, - char *fw_name) +static int ti_do_download(struct usb_device *dev, int pipe, + u8 *buffer, int size) { - const struct firmware *fw; - int status = 0; - int buffer_size; int pos; - int len; + u8 cs = 0; int done; - __u8 cs = 0; - __u8 *buffer; - struct usb_device *dev = tdev->td_serial->dev; struct ti_firmware_header *header; - unsigned int pipe = usb_sndbulkpipe(dev, - tdev->td_serial->port[0]->bulk_out_endpointAddress); - - buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header); - - if (request_firmware(&fw, fw_name, &dev->dev)) { - dev_err(&dev->dev, "%s - failed to load firmware \"%s\"\n", - __func__, fw_name); - return -ENOENT; - } - if (fw->size > buffer_size) { - dev_err(&dev->dev, "%s - firmware \"%s\" is too large\n", - __func__, fw_name); - release_firmware(fw); - return -EINVAL; - } - - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) { - dev_err(&dev->dev, "%s - out of memory\n", __func__); - release_firmware(fw); - return -ENOMEM; - } - - memcpy(buffer, fw->data, fw->size); - memset(buffer+fw->size, 0xff, buffer_size-fw->size); - - for(pos = sizeof(struct ti_firmware_header); pos < buffer_size; pos++) + int status; + int len; + + for(pos = sizeof(struct ti_firmware_header); pos < size; pos++) cs = (__u8)(cs + buffer[pos]); header = (struct ti_firmware_header *)buffer; - header->wLength = cpu_to_le16((__u16)(buffer_size - sizeof(struct ti_firmware_header))); + header->wLength = cpu_to_le16((__u16)(size + - sizeof(struct ti_firmware_header))); header->bCheckSum = cs; dbg("%s - downloading firmware", __func__); - for (pos = 0; pos < buffer_size; pos += done) { - len = min(buffer_size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE); - status = usb_bulk_msg(dev, pipe, buffer+pos, len, &done, 1000); + for (pos = 0; pos < size; pos += done) { + len = min(size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE); + status = usb_bulk_msg(dev, pipe, buffer + pos, len, + &done, 1000); if (status) break; } + return status; +} - kfree(buffer); - release_firmware(fw); +static int ti_download_firmware(struct ti_device *tdev, int type) +{ + int status = -ENOMEM; + int buffer_size; + __u8 *buffer; + struct usb_device *dev = tdev->td_serial->dev; + unsigned int pipe = usb_sndbulkpipe(dev, + tdev->td_serial->port[0]->bulk_out_endpointAddress); + const struct firmware *fw_p; + char buf[32]; + sprintf(buf, "ti_usb-%d.bin", type); + if (request_firmware(&fw_p, buf, &dev->dev)) { + dev_err(&dev->dev, "%s - firmware not found\n", __func__); + return -ENOENT; + } + if (fw_p->size > TI_FIRMWARE_BUF_SIZE) { + dev_err(&dev->dev, "%s - firmware too large\n", __func__); + return -ENOENT; + } + + buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header); + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (buffer) { + memcpy(buffer, fw_p->data, fw_p->size); + memset(buffer + fw_p->size, 0xff, buffer_size - fw_p->size); + ti_do_download(dev, pipe, buffer, fw_p->size); + kfree(buffer); + } + release_firmware(fw_p); if (status) { dev_err(&dev->dev, "%s - error downloading firmware, %d\n", __func__, status); return status; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 353798631903..ffaed8ace066 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -143,7 +143,7 @@ static void destroy_serial(struct kref *kref) return_serial(serial); for (i = 0; i < serial->num_ports; ++i) - serial->port[i]->open_count = 0; + serial->port[i]->port.count = 0; /* the ports are cleaned up and released in port_release() */ for (i = 0; i < serial->num_ports; ++i) @@ -208,14 +208,14 @@ static int serial_open (struct tty_struct *tty, struct file * filp) goto bailout_kref_put; } - ++port->open_count; + ++port->port.count; /* set up our port structure making the tty driver * remember our port object, and us it */ tty->driver_data = port; - port->tty = tty; + port->port.tty = tty; - if (port->open_count == 1) { + if (port->port.count == 1) { /* lock this module before we call it * this may fail, which means we must bail out, @@ -230,7 +230,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp) goto bailout_module_put; /* only call the device specific open if this * is the first time the port is opened */ - retval = serial->type->open(port, filp); + retval = serial->type->open(tty, port, filp); if (retval) goto bailout_interface_put; } @@ -243,9 +243,9 @@ bailout_interface_put: bailout_module_put: module_put(serial->type->driver.owner); bailout_mutex_unlock: - port->open_count = 0; + port->port.count = 0; tty->driver_data = NULL; - port->tty = NULL; + port->port.tty = NULL; mutex_unlock(&port->mutex); bailout_kref_put: usb_serial_put(serial); @@ -263,26 +263,26 @@ static void serial_close(struct tty_struct *tty, struct file * filp) mutex_lock(&port->mutex); - if (port->open_count == 0) { + if (port->port.count == 0) { mutex_unlock(&port->mutex); return; } - --port->open_count; - if (port->open_count == 0) + --port->port.count; + if (port->port.count == 0) /* only call the device specific close if this * port is being closed by the last owner */ - port->serial->type->close(port, filp); + port->serial->type->close(tty, port, filp); - if (port->open_count == (port->console? 1 : 0)) { - if (port->tty) { - if (port->tty->driver_data) - port->tty->driver_data = NULL; - port->tty = NULL; + if (port->port.count == (port->console? 1 : 0)) { + if (port->port.tty) { + if (port->port.tty->driver_data) + port->port.tty->driver_data = NULL; + port->port.tty = NULL; } } - if (port->open_count == 0) { + if (port->port.count == 0) { mutex_lock(&port->serial->disc_mutex); if (!port->serial->disconnected) usb_autopm_put_interface(port->serial->interface); @@ -304,12 +304,12 @@ static int serial_write (struct tty_struct * tty, const unsigned char *buf, int dbg("%s - port %d, %d byte(s)", __func__, port->number, count); - /* open_count is managed under the mutex lock for the tty so cannot + /* count is managed under the mutex lock for the tty so cannot drop to zero until after the last close completes */ - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ - retval = port->serial->type->write(port, buf, count); + retval = port->serial->type->write(tty, port, buf, count); exit: return retval; @@ -319,9 +319,9 @@ static int serial_write_room (struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ - return port->serial->type->write_room(port); + return port->serial->type->write_room(tty); } static int serial_chars_in_buffer (struct tty_struct *tty) @@ -329,9 +329,9 @@ static int serial_chars_in_buffer (struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; dbg("%s = port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ - return port->serial->type->chars_in_buffer(port); + return port->serial->type->chars_in_buffer(tty); } static void serial_throttle (struct tty_struct * tty) @@ -339,10 +339,10 @@ static void serial_throttle (struct tty_struct * tty) struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ if (port->serial->type->throttle) - port->serial->type->throttle(port); + port->serial->type->throttle(tty); } static void serial_unthrottle (struct tty_struct * tty) @@ -350,10 +350,10 @@ static void serial_unthrottle (struct tty_struct * tty) struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function */ if (port->serial->type->unthrottle) - port->serial->type->unthrottle(port); + port->serial->type->unthrottle(tty); } static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) @@ -363,12 +363,12 @@ static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned in dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->ioctl) { lock_kernel(); - retval = port->serial->type->ioctl(port, file, cmd, arg); + retval = port->serial->type->ioctl(tty, file, cmd, arg); unlock_kernel(); } else @@ -381,10 +381,10 @@ static void serial_set_termios (struct tty_struct *tty, struct ktermios * old) struct usb_serial_port *port = tty->driver_data; dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->set_termios) - port->serial->type->set_termios(port, old); + port->serial->type->set_termios(tty, port, old); else tty_termios_copy_hw(tty->termios, old); } @@ -395,11 +395,11 @@ static void serial_break (struct tty_struct *tty, int break_state) dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); /* pass on to the driver specific version of this function if it is available */ if (port->serial->type->break_ctl) { lock_kernel(); - port->serial->type->break_ctl(port, break_state); + port->serial->type->break_ctl(tty, break_state); unlock_kernel(); } } @@ -457,9 +457,9 @@ static int serial_tiocmget (struct tty_struct *tty, struct file *file) dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); if (port->serial->type->tiocmget) - return port->serial->type->tiocmget(port, file); + return port->serial->type->tiocmget(tty, file); return -EINVAL; } @@ -470,9 +470,9 @@ static int serial_tiocmset (struct tty_struct *tty, struct file *file, dbg("%s - port %d", __func__, port->number); - WARN_ON(!port->open_count); + WARN_ON(!port->port.count); if (port->serial->type->tiocmset) - return port->serial->type->tiocmset(port, file, set, clear); + return port->serial->type->tiocmset(tty, file, set, clear); return -EINVAL; } @@ -497,7 +497,7 @@ static void usb_serial_port_work(struct work_struct *work) if (!port) return; - tty = port->tty; + tty = port->port.tty; if (!tty) return; @@ -1010,8 +1010,8 @@ void usb_serial_disconnect(struct usb_interface *interface) for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (port) { - if (port->tty) - tty_hangup(port->tty); + if (port->port.tty) + tty_hangup(port->port.tty); kill_traffic(port); } } diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index 9ca4d4db1ddd..fc5d9952b03b 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -31,10 +31,11 @@ static struct usb_driver debug_driver = { .no_dynamic_id = 1, }; -int usb_debug_open(struct usb_serial_port *port, struct file *filp) +int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) { port->bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE; - return usb_serial_generic_open(port, filp); + return usb_serial_generic_open(tty, port, filp); } static struct usb_serial_driver debug_device = { diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 5fc20122145f..373a3c7ea77b 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -35,17 +35,15 @@ #define DRIVER_DESC "USB HandSpring Visor / Palm OS driver" /* function prototypes for a handspring visor */ -static int visor_open (struct usb_serial_port *port, struct file *filp); -static void visor_close (struct usb_serial_port *port, struct file *filp); -static int visor_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int visor_write_room (struct usb_serial_port *port); -static int visor_chars_in_buffer (struct usb_serial_port *port); -static void visor_throttle (struct usb_serial_port *port); -static void visor_unthrottle (struct usb_serial_port *port); +static int visor_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void visor_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int visor_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); +static int visor_write_room (struct tty_struct *tty); +static void visor_throttle (struct tty_struct *tty); +static void visor_unthrottle (struct tty_struct *tty); static int visor_probe (struct usb_serial *serial, const struct usb_device_id *id); static int visor_calc_num_ports(struct usb_serial *serial); static void visor_shutdown (struct usb_serial *serial); -static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); static void visor_write_bulk_callback (struct urb *urb); static void visor_read_bulk_callback (struct urb *urb); static void visor_read_int_callback (struct urb *urb); @@ -198,10 +196,8 @@ static struct usb_serial_driver handspring_device = { .probe = visor_probe, .calc_num_ports = visor_calc_num_ports, .shutdown = visor_shutdown, - .ioctl = visor_ioctl, .write = visor_write, .write_room = visor_write_room, - .chars_in_buffer = visor_chars_in_buffer, .write_bulk_callback = visor_write_bulk_callback, .read_bulk_callback = visor_read_bulk_callback, .read_int_callback = visor_read_int_callback, @@ -225,10 +221,8 @@ static struct usb_serial_driver clie_5_device = { .probe = visor_probe, .calc_num_ports = visor_calc_num_ports, .shutdown = visor_shutdown, - .ioctl = visor_ioctl, .write = visor_write, .write_room = visor_write_room, - .chars_in_buffer = visor_chars_in_buffer, .write_bulk_callback = visor_write_bulk_callback, .read_bulk_callback = visor_read_bulk_callback, .read_int_callback = visor_read_int_callback, @@ -249,10 +243,8 @@ static struct usb_serial_driver clie_3_5_device = { .throttle = visor_throttle, .unthrottle = visor_unthrottle, .attach = clie_3_5_startup, - .ioctl = visor_ioctl, .write = visor_write, .write_room = visor_write_room, - .chars_in_buffer = visor_chars_in_buffer, .write_bulk_callback = visor_write_bulk_callback, .read_bulk_callback = visor_read_bulk_callback, }; @@ -274,7 +266,7 @@ static int stats; /****************************************************************************** * Handspring Visor specific driver functions ******************************************************************************/ -static int visor_open (struct usb_serial_port *port, struct file *filp) +static int visor_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct visor_private *priv = usb_get_serial_port_data(port); @@ -300,8 +292,8 @@ static int visor_open (struct usb_serial_port *port, struct file *filp) * through, otherwise it is scheduled, and with high data rates (like * with OHCI) data can get lost. */ - if (port->tty) - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* Start reading from the device */ usb_fill_bulk_urb (port->read_urb, serial->dev, @@ -329,7 +321,8 @@ exit: } -static void visor_close (struct usb_serial_port *port, struct file * filp) +static void visor_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct visor_private *priv = usb_get_serial_port_data(port); unsigned char *transfer_buffer; @@ -361,7 +354,8 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) } -static int visor_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int visor_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count) { struct visor_private *priv = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -435,8 +429,9 @@ error_no_buffer: } -static int visor_write_room (struct usb_serial_port *port) +static int visor_write_room (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -460,22 +455,6 @@ static int visor_write_room (struct usb_serial_port *port) } -static int visor_chars_in_buffer (struct usb_serial_port *port) -{ - dbg("%s - port %d", __func__, port->number); - - /* - * We can't really account for how much data we - * have sent out, but hasn't made it through to the - * device, so just tell the tty layer that everything - * is flushed. - * - * FIXME: Should walk outstanding_urbs - */ - return 0; -} - - static void visor_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = urb->context; @@ -520,7 +499,7 @@ static void visor_read_bulk_callback (struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->tty; + tty = port->port.tty; if (tty && urb->actual_length) { available_room = tty_buffer_request_room(tty, urb->actual_length); if (available_room) { @@ -591,8 +570,9 @@ exit: __func__, result); } -static void visor_throttle (struct usb_serial_port *port) +static void visor_throttle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); unsigned long flags; @@ -603,8 +583,9 @@ static void visor_throttle (struct usb_serial_port *port) } -static void visor_unthrottle (struct usb_serial_port *port) +static void visor_unthrottle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); unsigned long flags; int result; @@ -922,13 +903,6 @@ static void visor_shutdown (struct usb_serial *serial) } } -static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) -{ - dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd); - - return -ENOIOCTLCMD; -} - static int __init visor_init (void) { int i, retval; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 665aa77a917b..b07d6a5cac31 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -142,18 +142,18 @@ static int whiteheat_firmware_attach (struct usb_serial *serial); /* function prototypes for the Connect Tech WhiteHEAT serial converter */ static int whiteheat_attach (struct usb_serial *serial); static void whiteheat_shutdown (struct usb_serial *serial); -static int whiteheat_open (struct usb_serial_port *port, struct file *filp); -static void whiteheat_close (struct usb_serial_port *port, struct file *filp); -static int whiteheat_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static int whiteheat_write_room (struct usb_serial_port *port); -static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -static void whiteheat_set_termios (struct usb_serial_port *port, struct ktermios * old); -static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file); -static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); -static void whiteheat_break_ctl (struct usb_serial_port *port, int break_state); -static int whiteheat_chars_in_buffer (struct usb_serial_port *port); -static void whiteheat_throttle (struct usb_serial_port *port); -static void whiteheat_unthrottle (struct usb_serial_port *port); +static int whiteheat_open (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static void whiteheat_close (struct tty_struct *tty, struct usb_serial_port *port, struct file *filp); +static int whiteheat_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); +static int whiteheat_write_room (struct tty_struct *tty); +static int whiteheat_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); +static void whiteheat_set_termios (struct tty_struct *tty, struct usb_serial_port *port, struct ktermios * old); +static int whiteheat_tiocmget (struct tty_struct *tty, struct file *file); +static int whiteheat_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); +static void whiteheat_break_ctl (struct tty_struct *tty, int break_state); +static int whiteheat_chars_in_buffer (struct tty_struct *tty); +static void whiteheat_throttle (struct tty_struct *tty); +static void whiteheat_unthrottle (struct tty_struct *tty); static void whiteheat_read_callback (struct urb *urb); static void whiteheat_write_callback (struct urb *urb); @@ -246,7 +246,7 @@ static void rx_data_softint(struct work_struct *work); static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize); static int firm_open(struct usb_serial_port *port); static int firm_close(struct usb_serial_port *port); -static int firm_setup_port(struct usb_serial_port *port); +static int firm_setup_port(struct tty_struct *tty); static int firm_set_rts(struct usb_serial_port *port, __u8 onoff); static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff); static int firm_set_break(struct usb_serial_port *port, __u8 onoff); @@ -613,7 +613,8 @@ static void whiteheat_shutdown (struct usb_serial *serial) } -static int whiteheat_open (struct usb_serial_port *port, struct file *filp) +static int whiteheat_open (struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) { int retval = 0; struct ktermios old_term; @@ -624,7 +625,8 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) if (retval) goto exit; - port->tty->low_latency = 1; + if (tty) + tty->low_latency = 1; /* send an open port command */ retval = firm_open(port); @@ -640,9 +642,11 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) goto exit; } - old_term.c_cflag = ~port->tty->termios->c_cflag; - old_term.c_iflag = ~port->tty->termios->c_iflag; - whiteheat_set_termios(port, &old_term); + if (tty) { + old_term.c_cflag = ~tty->termios->c_cflag; + old_term.c_iflag = ~tty->termios->c_iflag; + whiteheat_set_termios(tty, port, &old_term); + } /* Work around HCD bugs */ usb_clear_halt(port->serial->dev, port->read_urb->pipe); @@ -663,7 +667,8 @@ exit: } -static void whiteheat_close(struct usb_serial_port *port, struct file * filp) +static void whiteheat_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file * filp) { struct whiteheat_private *info = usb_get_serial_port_data(port); struct whiteheat_urb_wrap *wrap; @@ -681,7 +686,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) } mutex_unlock(&port->serial->disc_mutex); - port->tty->closing = 1; + tty->closing = 1; /* * Not currently in use; tty_wait_until_sent() calls @@ -689,12 +694,12 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) * acquisition. This should be fixed at some point. Greg's been * notified. if ((filp->f_flags & (O_NDELAY | O_NONBLOCK)) == 0) { - tty_wait_until_sent(port->tty, CLOSING_DELAY); + tty_wait_until_sent(tty, CLOSING_DELAY); } */ - tty_driver_flush_buffer(port->tty); - tty_ldisc_flush(port->tty); + tty_driver_flush_buffer(tty); + tty_ldisc_flush(tty); firm_report_tx_done(port); @@ -728,11 +733,12 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) stop_command_port(port->serial); - port->tty->closing = 0; + tty->closing = 0; } -static int whiteheat_write(struct usb_serial_port *port, const unsigned char *buf, int count) +static int whiteheat_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; struct whiteheat_private *info = usb_get_serial_port_data(port); @@ -791,8 +797,9 @@ static int whiteheat_write(struct usb_serial_port *port, const unsigned char *bu } -static int whiteheat_write_room(struct usb_serial_port *port) +static int whiteheat_write_room(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); struct list_head *tmp; int room = 0; @@ -811,8 +818,9 @@ static int whiteheat_write_room(struct usb_serial_port *port) } -static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file) +static int whiteheat_tiocmget (struct tty_struct *tty, struct file *file) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); unsigned int modem_signals = 0; @@ -828,9 +836,10 @@ static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file) } -static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, +static int whiteheat_tiocmset (struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); dbg("%s - port %d", __func__, port->number); @@ -851,8 +860,9 @@ static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, } -static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +static int whiteheat_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { + struct usb_serial_port *port = tty->driver_data; struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; @@ -896,20 +906,21 @@ static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, un } -static void whiteheat_set_termios(struct usb_serial_port *port, struct ktermios *old_termios) +static void whiteheat_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { - dbg("%s -port %d", __func__, port->number); - firm_setup_port(port); + firm_setup_port(tty); } -static void whiteheat_break_ctl(struct usb_serial_port *port, int break_state) { +static void whiteheat_break_ctl(struct tty_struct *tty, int break_state) { + struct usb_serial_port *port = tty->driver_data; firm_set_break(port, break_state); } -static int whiteheat_chars_in_buffer(struct usb_serial_port *port) +static int whiteheat_chars_in_buffer(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); struct list_head *tmp; struct whiteheat_urb_wrap *wrap; @@ -930,8 +941,9 @@ static int whiteheat_chars_in_buffer(struct usb_serial_port *port) } -static void whiteheat_throttle (struct usb_serial_port *port) +static void whiteheat_throttle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); unsigned long flags; @@ -945,8 +957,9 @@ static void whiteheat_throttle (struct usb_serial_port *port) } -static void whiteheat_unthrottle (struct usb_serial_port *port) +static void whiteheat_unthrottle (struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); int actually_throttled; unsigned long flags; @@ -1184,9 +1197,10 @@ static int firm_close(struct usb_serial_port *port) { } -static int firm_setup_port(struct usb_serial_port *port) { +static int firm_setup_port(struct tty_struct *tty) { + struct usb_serial_port *port = tty->driver_data; struct whiteheat_port_settings port_settings; - unsigned int cflag = port->tty->termios->c_cflag; + unsigned int cflag = tty->termios->c_cflag; port_settings.port = port->number + 1; @@ -1235,22 +1249,22 @@ static int firm_setup_port(struct usb_serial_port *port) { (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : ""); /* determine software flow control */ - if (I_IXOFF(port->tty)) + if (I_IXOFF(tty)) port_settings.sflow = WHITEHEAT_SFLOW_RXTX; else port_settings.sflow = WHITEHEAT_SFLOW_NONE; dbg("%s - software flow control = %c", __func__, port_settings.sflow); - port_settings.xon = START_CHAR(port->tty); - port_settings.xoff = STOP_CHAR(port->tty); + port_settings.xon = START_CHAR(tty); + port_settings.xoff = STOP_CHAR(tty); dbg("%s - XON = %2x, XOFF = %2x", __func__, port_settings.xon, port_settings.xoff); /* get the baud rate wanted */ - port_settings.baud = tty_get_baud_rate(port->tty); + port_settings.baud = tty_get_baud_rate(tty); dbg("%s - baud rate = %d", __func__, port_settings.baud); /* fixme: should set validated settings */ - tty_encode_baud_rate(port->tty, port_settings.baud, port_settings.baud); + tty_encode_baud_rate(tty, port_settings.baud, port_settings.baud); /* handle any settings that aren't specified in the tty structure */ port_settings.lloop = 0; @@ -1426,7 +1440,7 @@ static void rx_data_softint(struct work_struct *work) struct whiteheat_private *info = container_of(work, struct whiteheat_private, rx_work); struct usb_serial_port *port = info->port; - struct tty_struct *tty = port->tty; + struct tty_struct *tty = port->port.tty; struct whiteheat_urb_wrap *wrap; struct urb *urb; unsigned long flags; diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 8f891cbaf9ab..09a3e6a7518f 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -62,7 +62,7 @@ */ struct usb_serial_port { struct usb_serial *serial; - struct tty_struct *tty; + struct tty_port port; spinlock_t lock; struct mutex mutex; unsigned char number; @@ -89,7 +89,6 @@ struct usb_serial_port { wait_queue_head_t write_wait; struct work_struct work; - int open_count; char throttled; char throttle_req; char console; @@ -217,22 +216,27 @@ struct usb_serial_driver { int (*resume)(struct usb_serial *serial); /* serial function calls */ - int (*open)(struct usb_serial_port *port, struct file *filp); - void (*close)(struct usb_serial_port *port, struct file *filp); - int (*write)(struct usb_serial_port *port, const unsigned char *buf, - int count); - int (*write_room)(struct usb_serial_port *port); - int (*ioctl)(struct usb_serial_port *port, struct file *file, + /* Called by console with tty = NULL and by tty */ + int (*open)(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + void (*close)(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + int (*write)(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); + /* Called only by the tty layer */ + int (*write_room)(struct tty_struct *tty); + int (*ioctl)(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); - void (*set_termios)(struct usb_serial_port *port, struct ktermios *old); - void (*break_ctl)(struct usb_serial_port *port, int break_state); - int (*chars_in_buffer)(struct usb_serial_port *port); - void (*throttle)(struct usb_serial_port *port); - void (*unthrottle)(struct usb_serial_port *port); - int (*tiocmget)(struct usb_serial_port *port, struct file *file); - int (*tiocmset)(struct usb_serial_port *port, struct file *file, + void (*set_termios)(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old); + void (*break_ctl)(struct tty_struct *tty, int break_state); + int (*chars_in_buffer)(struct tty_struct *tty); + void (*throttle)(struct tty_struct *tty); + void (*unthrottle)(struct tty_struct *tty); + int (*tiocmget)(struct tty_struct *tty, struct file *file); + int (*tiocmset)(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); - + /* USB events */ void (*read_int_callback)(struct urb *urb); void (*write_int_callback)(struct urb *urb); void (*read_bulk_callback)(struct urb *urb); @@ -270,19 +274,19 @@ static inline void usb_serial_console_disconnect(struct usb_serial *serial) {} /* Functions needed by other parts of the usbserial core */ extern struct usb_serial *usb_serial_get_by_index(unsigned int minor); extern void usb_serial_put(struct usb_serial *serial); -extern int usb_serial_generic_open(struct usb_serial_port *port, - struct file *filp); -extern int usb_serial_generic_write(struct usb_serial_port *port, - const unsigned char *buf, int count); -extern void usb_serial_generic_close(struct usb_serial_port *port, - struct file *filp); +extern int usb_serial_generic_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); +extern int usb_serial_generic_write(struct tty_struct *tty, + struct usb_serial_port *port, const unsigned char *buf, int count); +extern void usb_serial_generic_close(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); extern int usb_serial_generic_resume(struct usb_serial *serial); -extern int usb_serial_generic_write_room(struct usb_serial_port *port); -extern int usb_serial_generic_chars_in_buffer(struct usb_serial_port *port); +extern int usb_serial_generic_write_room(struct tty_struct *tty); +extern int usb_serial_generic_chars_in_buffer(struct tty_struct *tty); extern void usb_serial_generic_read_bulk_callback(struct urb *urb); extern void usb_serial_generic_write_bulk_callback(struct urb *urb); -extern void usb_serial_generic_throttle(struct usb_serial_port *port); -extern void usb_serial_generic_unthrottle(struct usb_serial_port *port); +extern void usb_serial_generic_throttle(struct tty_struct *tty); +extern void usb_serial_generic_unthrottle(struct tty_struct *tty); extern void usb_serial_generic_shutdown(struct usb_serial *serial); extern int usb_serial_generic_register(int debug); extern void usb_serial_generic_deregister(void); -- cgit From 01e1abb2c27e43339b8829a2e3b1c6f53806b77a Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 22 Jul 2008 11:16:55 +0100 Subject: tty: Split ldisc code into its own file Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds --- drivers/char/Makefile | 2 +- drivers/char/tty_io.c | 636 +---------------------------------------- drivers/char/tty_ldisc.c | 714 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/tty.h | 11 +- 4 files changed, 733 insertions(+), 630 deletions(-) create mode 100644 drivers/char/tty_ldisc.c (limited to 'include/linux') diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 0e0d12a06462..dc5a327d72d5 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -7,7 +7,7 @@ # FONTMAPFILE = cp437.uni -obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o +obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 82f6a8c86332..d27a08b374d0 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -655,558 +655,6 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); -/** - * tty_set_termios_ldisc - set ldisc field - * @tty: tty structure - * @num: line discipline number - * - * This is probably overkill for real world processors but - * they are not on hot paths so a little discipline won't do - * any harm. - * - * Locking: takes termios_mutex - */ - -static void tty_set_termios_ldisc(struct tty_struct *tty, int num) -{ - mutex_lock(&tty->termios_mutex); - tty->termios->c_line = num; - mutex_unlock(&tty->termios_mutex); -} - -/* - * This guards the refcounted line discipline lists. The lock - * must be taken with irqs off because there are hangup path - * callers who will do ldisc lookups and cannot sleep. - */ - -static DEFINE_SPINLOCK(tty_ldisc_lock); -static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); -/* Line disc dispatch table */ -static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; - -/** - * tty_register_ldisc - install a line discipline - * @disc: ldisc number - * @new_ldisc: pointer to the ldisc object - * - * Installs a new line discipline into the kernel. The discipline - * is set up as unreferenced and then made available to the kernel - * from this point onwards. - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) -{ - unsigned long flags; - int ret = 0; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - tty_ldiscs[disc] = new_ldisc; - new_ldisc->num = disc; - new_ldisc->refcount = 0; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - return ret; -} -EXPORT_SYMBOL(tty_register_ldisc); - -/** - * tty_unregister_ldisc - unload a line discipline - * @disc: ldisc number - * @new_ldisc: pointer to the ldisc object - * - * Remove a line discipline from the kernel providing it is not - * currently in use. - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -int tty_unregister_ldisc(int disc) -{ - unsigned long flags; - int ret = 0; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - if (tty_ldiscs[disc]->refcount) - ret = -EBUSY; - else - tty_ldiscs[disc] = NULL; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - return ret; -} -EXPORT_SYMBOL(tty_unregister_ldisc); - - -/** - * tty_ldisc_try_get - try and reference an ldisc - * @disc: ldisc number - * @ld: tty ldisc structure to complete - * - * Attempt to open and lock a line discipline into place. Return - * the line discipline refcounted and assigned in ld. On an error - * report the error code back - */ - -static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld) -{ - unsigned long flags; - struct tty_ldisc_ops *ldops; - int err = -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld->ops = NULL; - ldops = tty_ldiscs[disc]; - /* Check the entry is defined */ - if (ldops) { - /* If the module is being unloaded we can't use it */ - if (!try_module_get(ldops->owner)) - err = -EAGAIN; - else { - /* lock it */ - ldops->refcount++; - ld->ops = ldops; - err = 0; - } - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return err; -} - -/** - * tty_ldisc_get - take a reference to an ldisc - * @disc: ldisc number - * @ld: tty line discipline structure to use - * - * Takes a reference to a line discipline. Deals with refcounts and - * module locking counts. Returns NULL if the discipline is not available. - * Returns a pointer to the discipline and bumps the ref count if it is - * available - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -static int tty_ldisc_get(int disc, struct tty_ldisc *ld) -{ - int err; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - err = tty_ldisc_try_get(disc, ld); - if (err == -EAGAIN) { - request_module("tty-ldisc-%d", disc); - err = tty_ldisc_try_get(disc, ld); - } - return err; -} - -/** - * tty_ldisc_put - drop ldisc reference - * @disc: ldisc number - * - * Drop a reference to a line discipline. Manage refcounts and - * module usage counts - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -static void tty_ldisc_put(struct tty_ldisc_ops *ld) -{ - unsigned long flags; - int disc = ld->num; - - BUG_ON(disc < N_TTY || disc >= NR_LDISCS); - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = tty_ldiscs[disc]; - BUG_ON(ld->refcount == 0); - ld->refcount--; - module_put(ld->owner); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); -} - -static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) -{ - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) -{ - (*pos)++; - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) -{ -} - -static int tty_ldiscs_seq_show(struct seq_file *m, void *v) -{ - int i = *(loff_t *)v; - struct tty_ldisc ld; - - if (tty_ldisc_get(i, &ld) < 0) - return 0; - seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i); - tty_ldisc_put(ld.ops); - return 0; -} - -static const struct seq_operations tty_ldiscs_seq_ops = { - .start = tty_ldiscs_seq_start, - .next = tty_ldiscs_seq_next, - .stop = tty_ldiscs_seq_stop, - .show = tty_ldiscs_seq_show, -}; - -static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &tty_ldiscs_seq_ops); -} - -const struct file_operations tty_ldiscs_proc_fops = { - .owner = THIS_MODULE, - .open = proc_tty_ldiscs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -/** - * tty_ldisc_assign - set ldisc on a tty - * @tty: tty to assign - * @ld: line discipline - * - * Install an instance of a line discipline into a tty structure. The - * ldisc must have a reference count above zero to ensure it remains/ - * The tty instance refcount starts at zero. - * - * Locking: - * Caller must hold references - */ - -static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) -{ - ld->refcount = 0; - tty->ldisc = *ld; -} - -/** - * tty_ldisc_try - internal helper - * @tty: the tty - * - * Make a single attempt to grab and bump the refcount on - * the tty ldisc. Return 0 on failure or 1 on success. This is - * used to implement both the waiting and non waiting versions - * of tty_ldisc_ref - * - * Locking: takes tty_ldisc_lock - */ - -static int tty_ldisc_try(struct tty_struct *tty) -{ - unsigned long flags; - struct tty_ldisc *ld; - int ret = 0; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = &tty->ldisc; - if (test_bit(TTY_LDISC, &tty->flags)) { - ld->refcount++; - ret = 1; - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return ret; -} - -/** - * tty_ldisc_ref_wait - wait for the tty ldisc - * @tty: tty device - * - * Dereference the line discipline for the terminal and take a - * reference to it. If the line discipline is in flux then - * wait patiently until it changes. - * - * Note: Must not be called from an IRQ/timer context. The caller - * must also be careful not to hold other locks that will deadlock - * against a discipline change, such as an existing ldisc reference - * (which we check for) - * - * Locking: call functions take tty_ldisc_lock - */ - -struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) -{ - /* wait_event is a macro */ - wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); - if (tty->ldisc.refcount == 0) - printk(KERN_ERR "tty_ldisc_ref_wait\n"); - return &tty->ldisc; -} - -EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); - -/** - * tty_ldisc_ref - get the tty ldisc - * @tty: tty device - * - * Dereference the line discipline for the terminal and take a - * reference to it. If the line discipline is in flux then - * return NULL. Can be called from IRQ and timer functions. - * - * Locking: called functions take tty_ldisc_lock - */ - -struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) -{ - if (tty_ldisc_try(tty)) - return &tty->ldisc; - return NULL; -} - -EXPORT_SYMBOL_GPL(tty_ldisc_ref); - -/** - * tty_ldisc_deref - free a tty ldisc reference - * @ld: reference to free up - * - * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May - * be called in IRQ context. - * - * Locking: takes tty_ldisc_lock - */ - -void tty_ldisc_deref(struct tty_ldisc *ld) -{ - unsigned long flags; - - BUG_ON(ld == NULL); - - spin_lock_irqsave(&tty_ldisc_lock, flags); - if (ld->refcount == 0) - printk(KERN_ERR "tty_ldisc_deref: no references.\n"); - else - ld->refcount--; - if (ld->refcount == 0) - wake_up(&tty_ldisc_wait); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); -} - -EXPORT_SYMBOL_GPL(tty_ldisc_deref); - -/** - * tty_ldisc_enable - allow ldisc use - * @tty: terminal to activate ldisc on - * - * Set the TTY_LDISC flag when the line discipline can be called - * again. Do necessary wakeups for existing sleepers. - * - * Note: nobody should set this bit except via this function. Clearing - * directly is allowed. - */ - -static void tty_ldisc_enable(struct tty_struct *tty) -{ - set_bit(TTY_LDISC, &tty->flags); - wake_up(&tty_ldisc_wait); -} - -/** - * tty_ldisc_restore - helper for tty ldisc change - * @tty: tty to recover - * @old: previous ldisc - * - * Restore the previous line discipline or N_TTY when a line discipline - * change fails due to an open error - */ - -static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) -{ - char buf[64]; - struct tty_ldisc new_ldisc; - - /* There is an outstanding reference here so this is safe */ - tty_ldisc_get(old->ops->num, old); - tty_ldisc_assign(tty, old); - tty_set_termios_ldisc(tty, old->ops->num); - if (old->ops->open && (old->ops->open(tty) < 0)) { - tty_ldisc_put(old->ops); - /* This driver is always present */ - if (tty_ldisc_get(N_TTY, &new_ldisc) < 0) - panic("n_tty: get"); - tty_ldisc_assign(tty, &new_ldisc); - tty_set_termios_ldisc(tty, N_TTY); - if (new_ldisc.ops->open) { - int r = new_ldisc.ops->open(tty); - if (r < 0) - panic("Couldn't open N_TTY ldisc for " - "%s --- error %d.", - tty_name(tty, buf), r); - } - } -} - -/** - * tty_set_ldisc - set line discipline - * @tty: the terminal to set - * @ldisc: the line discipline - * - * Set the discipline of a tty line. Must be called from a process - * context. - * - * Locking: takes tty_ldisc_lock. - * called functions take termios_mutex - */ - -static int tty_set_ldisc(struct tty_struct *tty, int ldisc) -{ - int retval; - struct tty_ldisc o_ldisc, new_ldisc; - int work; - unsigned long flags; - struct tty_struct *o_tty; - -restart: - /* This is a bit ugly for now but means we can break the 'ldisc - is part of the tty struct' assumption later */ - retval = tty_ldisc_get(ldisc, &new_ldisc); - if (retval) - return retval; - - /* - * Problem: What do we do if this blocks ? - */ - - tty_wait_until_sent(tty, 0); - - if (tty->ldisc.ops->num == ldisc) { - tty_ldisc_put(new_ldisc.ops); - return 0; - } - - /* - * No more input please, we are switching. The new ldisc - * will update this value in the ldisc open function - */ - - tty->receive_room = 0; - - o_ldisc = tty->ldisc; - o_tty = tty->link; - - /* - * Make sure we don't change while someone holds a - * reference to the line discipline. The TTY_LDISC bit - * prevents anyone taking a reference once it is clear. - * We need the lock to avoid racing reference takers. - */ - - spin_lock_irqsave(&tty_ldisc_lock, flags); - if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) { - if (tty->ldisc.refcount) { - /* Free the new ldisc we grabbed. Must drop the lock - first. */ - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(o_ldisc.ops); - /* - * There are several reasons we may be busy, including - * random momentary I/O traffic. We must therefore - * retry. We could distinguish between blocking ops - * and retries if we made tty_ldisc_wait() smarter. - * That is up for discussion. - */ - if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0) - return -ERESTARTSYS; - goto restart; - } - if (o_tty && o_tty->ldisc.refcount) { - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(o_tty->ldisc.ops); - if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0) - return -ERESTARTSYS; - goto restart; - } - } - /* - * If the TTY_LDISC bit is set, then we are racing against - * another ldisc change - */ - if (!test_bit(TTY_LDISC, &tty->flags)) { - struct tty_ldisc *ld; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(new_ldisc.ops); - ld = tty_ldisc_ref_wait(tty); - tty_ldisc_deref(ld); - goto restart; - } - - clear_bit(TTY_LDISC, &tty->flags); - if (o_tty) - clear_bit(TTY_LDISC, &o_tty->flags); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - /* - * From this point on we know nobody has an ldisc - * usage reference, nor can they obtain one until - * we say so later on. - */ - - work = cancel_delayed_work(&tty->buf.work); - /* - * Wait for ->hangup_work and ->buf.work handlers to terminate - * MUST NOT hold locks here. - */ - flush_scheduled_work(); - /* Shutdown the current discipline. */ - if (o_ldisc.ops->close) - (o_ldisc.ops->close)(tty); - - /* Now set up the new line discipline. */ - tty_ldisc_assign(tty, &new_ldisc); - tty_set_termios_ldisc(tty, ldisc); - if (new_ldisc.ops->open) - retval = (new_ldisc.ops->open)(tty); - if (retval < 0) { - tty_ldisc_put(new_ldisc.ops); - tty_ldisc_restore(tty, &o_ldisc); - } - /* At this point we hold a reference to the new ldisc and a - a reference to the old ldisc. If we ended up flipping back - to the existing ldisc we have two references to it */ - - if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc) - tty->ops->set_ldisc(tty); - - tty_ldisc_put(o_ldisc.ops); - - /* - * Allow ldisc referencing to occur as soon as the driver - * ldisc callback completes. - */ - - tty_ldisc_enable(tty); - if (o_tty) - tty_ldisc_enable(o_tty); - - /* Restart it in case no characters kick it off. Safe if - already running */ - if (work) - schedule_delayed_work(&tty->buf.work, 1); - return retval; -} - /** * get_tty_driver - find device of a tty * @dev_t: device identifier @@ -2193,7 +1641,6 @@ static int init_dev(struct tty_driver *driver, int idx, struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc; struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; int retval = 0; - struct tty_ldisc *ld; /* check whether we're reopening an existing tty */ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { @@ -2342,25 +1789,12 @@ static int init_dev(struct tty_driver *driver, int idx, * If we fail here just call release_tty to clean up. No need * to decrement the use counts, as release_tty doesn't care. */ - - ld = &tty->ldisc; - if (ld->ops->open) { - retval = (ld->ops->open)(tty); - if (retval) - goto release_mem_out; - } - if (o_tty && o_tty->ldisc.ops->open) { - retval = (o_tty->ldisc.ops->open)(o_tty); - if (retval) { - if (ld->ops->close) - (ld->ops->close)(tty); - goto release_mem_out; - } - tty_ldisc_enable(o_tty); - } - tty_ldisc_enable(tty); - goto success; + retval = tty_ldisc_setup(tty, o_tty); + + if (retval) + goto release_mem_out; + goto success; /* * This fast open can be used if the tty is already open. @@ -2498,12 +1932,10 @@ static void release_tty(struct tty_struct *tty, int idx) static void release_dev(struct file *filp) { struct tty_struct *tty, *o_tty; - struct tty_ldisc ld; int pty_master, tty_closing, o_tty_closing, do_sleep; int devpts; int idx; char buf[64]; - unsigned long flags; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, @@ -2705,56 +2137,9 @@ static void release_dev(struct file *filp) printk(KERN_DEBUG "freeing tty structure..."); #endif /* - * Prevent flush_to_ldisc() from rescheduling the work for later. Then - * kill any delayed work. As this is the final close it does not - * race with the set_ldisc code path. - */ - clear_bit(TTY_LDISC, &tty->flags); - cancel_delayed_work(&tty->buf.work); - - /* - * Wait for ->hangup_work and ->buf.work handlers to terminate - */ - - flush_scheduled_work(); - - /* - * Wait for any short term users (we know they are just driver - * side waiters as the file is closing so user count on the file - * side is zero. + * Ask the line discipline code to release its structures */ - spin_lock_irqsave(&tty_ldisc_lock, flags); - while (tty->ldisc.refcount) { - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0); - spin_lock_irqsave(&tty_ldisc_lock, flags); - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - /* - * Shutdown the current line discipline, and reset it to N_TTY. - * - * FIXME: this MUST get fixed for the new reflocking - */ - if (tty->ldisc.ops->close) - (tty->ldisc.ops->close)(tty); - tty_ldisc_put(tty->ldisc.ops); - - /* - * Switch the line discipline back - */ - WARN_ON(tty_ldisc_get(N_TTY, &ld)); - tty_ldisc_assign(tty, &ld); - tty_set_termios_ldisc(tty, N_TTY); - if (o_tty) { - /* FIXME: could o_tty be in setldisc here ? */ - clear_bit(TTY_LDISC, &o_tty->flags); - if (o_tty->ldisc.ops->close) - (o_tty->ldisc.ops->close)(o_tty); - tty_ldisc_put(o_tty->ldisc.ops); - WARN_ON(tty_ldisc_get(N_TTY, &ld)); - tty_ldisc_assign(o_tty, &ld); - tty_set_termios_ldisc(o_tty, N_TTY); - } + tty_ldisc_release(tty, o_tty); /* * The release_tty function takes care of the details of clearing * the slots and preserving the termios structure. @@ -3962,12 +3347,9 @@ EXPORT_SYMBOL(tty_flip_buffer_push); static void initialize_tty_struct(struct tty_struct *tty) { - struct tty_ldisc ld; memset(tty, 0, sizeof(struct tty_struct)); tty->magic = TTY_MAGIC; - if (tty_ldisc_get(N_TTY, &ld) < 0) - panic("n_tty: init_tty"); - tty_ldisc_assign(tty, &ld); + tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; tty->overrun_time = jiffies; @@ -4280,7 +3662,7 @@ void __init console_init(void) initcall_t *call; /* Setup the default TTY line discipline. */ - (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); + tty_ldisc_begin(); /* * set up the console device so that later boot sequences can diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c new file mode 100644 index 000000000000..241cbdea65ab --- /dev/null +++ b/drivers/char/tty_ldisc.c @@ -0,0 +1,714 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +/* + * This guards the refcounted line discipline lists. The lock + * must be taken with irqs off because there are hangup path + * callers who will do ldisc lookups and cannot sleep. + */ + +static DEFINE_SPINLOCK(tty_ldisc_lock); +static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); +/* Line disc dispatch table */ +static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; + +/** + * tty_register_ldisc - install a line discipline + * @disc: ldisc number + * @new_ldisc: pointer to the ldisc object + * + * Installs a new line discipline into the kernel. The discipline + * is set up as unreferenced and then made available to the kernel + * from this point onwards. + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) +{ + unsigned long flags; + int ret = 0; + + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + tty_ldiscs[disc] = new_ldisc; + new_ldisc->num = disc; + new_ldisc->refcount = 0; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + return ret; +} +EXPORT_SYMBOL(tty_register_ldisc); + +/** + * tty_unregister_ldisc - unload a line discipline + * @disc: ldisc number + * @new_ldisc: pointer to the ldisc object + * + * Remove a line discipline from the kernel providing it is not + * currently in use. + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +int tty_unregister_ldisc(int disc) +{ + unsigned long flags; + int ret = 0; + + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if (tty_ldiscs[disc]->refcount) + ret = -EBUSY; + else + tty_ldiscs[disc] = NULL; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + return ret; +} +EXPORT_SYMBOL(tty_unregister_ldisc); + + +/** + * tty_ldisc_try_get - try and reference an ldisc + * @disc: ldisc number + * @ld: tty ldisc structure to complete + * + * Attempt to open and lock a line discipline into place. Return + * the line discipline refcounted and assigned in ld. On an error + * report the error code back + */ + +static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld) +{ + unsigned long flags; + struct tty_ldisc_ops *ldops; + int err = -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld->ops = NULL; + ldops = tty_ldiscs[disc]; + /* Check the entry is defined */ + if (ldops) { + /* If the module is being unloaded we can't use it */ + if (!try_module_get(ldops->owner)) + err = -EAGAIN; + else { + /* lock it */ + ldops->refcount++; + ld->ops = ldops; + err = 0; + } + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return err; +} + +/** + * tty_ldisc_get - take a reference to an ldisc + * @disc: ldisc number + * @ld: tty line discipline structure to use + * + * Takes a reference to a line discipline. Deals with refcounts and + * module locking counts. Returns NULL if the discipline is not available. + * Returns a pointer to the discipline and bumps the ref count if it is + * available + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +static int tty_ldisc_get(int disc, struct tty_ldisc *ld) +{ + int err; + + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; + err = tty_ldisc_try_get(disc, ld); + if (err == -EAGAIN) { + request_module("tty-ldisc-%d", disc); + err = tty_ldisc_try_get(disc, ld); + } + return err; +} + +/** + * tty_ldisc_put - drop ldisc reference + * @disc: ldisc number + * + * Drop a reference to a line discipline. Manage refcounts and + * module usage counts + * + * Locking: + * takes tty_ldisc_lock to guard against ldisc races + */ + +static void tty_ldisc_put(struct tty_ldisc_ops *ld) +{ + unsigned long flags; + int disc = ld->num; + + BUG_ON(disc < N_TTY || disc >= NR_LDISCS); + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld = tty_ldiscs[disc]; + BUG_ON(ld->refcount == 0); + ld->refcount--; + module_put(ld->owner); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} + +static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) +{ + return (*pos < NR_LDISCS) ? pos : NULL; +} + +static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + (*pos)++; + return (*pos < NR_LDISCS) ? pos : NULL; +} + +static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) +{ +} + +static int tty_ldiscs_seq_show(struct seq_file *m, void *v) +{ + int i = *(loff_t *)v; + struct tty_ldisc ld; + + if (tty_ldisc_get(i, &ld) < 0) + return 0; + seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i); + tty_ldisc_put(ld.ops); + return 0; +} + +static const struct seq_operations tty_ldiscs_seq_ops = { + .start = tty_ldiscs_seq_start, + .next = tty_ldiscs_seq_next, + .stop = tty_ldiscs_seq_stop, + .show = tty_ldiscs_seq_show, +}; + +static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &tty_ldiscs_seq_ops); +} + +const struct file_operations tty_ldiscs_proc_fops = { + .owner = THIS_MODULE, + .open = proc_tty_ldiscs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/** + * tty_ldisc_assign - set ldisc on a tty + * @tty: tty to assign + * @ld: line discipline + * + * Install an instance of a line discipline into a tty structure. The + * ldisc must have a reference count above zero to ensure it remains/ + * The tty instance refcount starts at zero. + * + * Locking: + * Caller must hold references + */ + +static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) +{ + ld->refcount = 0; + tty->ldisc = *ld; +} + +/** + * tty_ldisc_try - internal helper + * @tty: the tty + * + * Make a single attempt to grab and bump the refcount on + * the tty ldisc. Return 0 on failure or 1 on success. This is + * used to implement both the waiting and non waiting versions + * of tty_ldisc_ref + * + * Locking: takes tty_ldisc_lock + */ + +static int tty_ldisc_try(struct tty_struct *tty) +{ + unsigned long flags; + struct tty_ldisc *ld; + int ret = 0; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld = &tty->ldisc; + if (test_bit(TTY_LDISC, &tty->flags)) { + ld->refcount++; + ret = 1; + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return ret; +} + +/** + * tty_ldisc_ref_wait - wait for the tty ldisc + * @tty: tty device + * + * Dereference the line discipline for the terminal and take a + * reference to it. If the line discipline is in flux then + * wait patiently until it changes. + * + * Note: Must not be called from an IRQ/timer context. The caller + * must also be careful not to hold other locks that will deadlock + * against a discipline change, such as an existing ldisc reference + * (which we check for) + * + * Locking: call functions take tty_ldisc_lock + */ + +struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) +{ + /* wait_event is a macro */ + wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); + if (tty->ldisc.refcount == 0) + printk(KERN_ERR "tty_ldisc_ref_wait\n"); + return &tty->ldisc; +} + +EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); + +/** + * tty_ldisc_ref - get the tty ldisc + * @tty: tty device + * + * Dereference the line discipline for the terminal and take a + * reference to it. If the line discipline is in flux then + * return NULL. Can be called from IRQ and timer functions. + * + * Locking: called functions take tty_ldisc_lock + */ + +struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) +{ + if (tty_ldisc_try(tty)) + return &tty->ldisc; + return NULL; +} + +EXPORT_SYMBOL_GPL(tty_ldisc_ref); + +/** + * tty_ldisc_deref - free a tty ldisc reference + * @ld: reference to free up + * + * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May + * be called in IRQ context. + * + * Locking: takes tty_ldisc_lock + */ + +void tty_ldisc_deref(struct tty_ldisc *ld) +{ + unsigned long flags; + + BUG_ON(ld == NULL); + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if (ld->refcount == 0) + printk(KERN_ERR "tty_ldisc_deref: no references.\n"); + else + ld->refcount--; + if (ld->refcount == 0) + wake_up(&tty_ldisc_wait); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} + +EXPORT_SYMBOL_GPL(tty_ldisc_deref); + +/** + * tty_ldisc_enable - allow ldisc use + * @tty: terminal to activate ldisc on + * + * Set the TTY_LDISC flag when the line discipline can be called + * again. Do necessary wakeups for existing sleepers. + * + * Note: nobody should set this bit except via this function. Clearing + * directly is allowed. + */ + +void tty_ldisc_enable(struct tty_struct *tty) +{ + set_bit(TTY_LDISC, &tty->flags); + wake_up(&tty_ldisc_wait); +} + +/** + * tty_set_termios_ldisc - set ldisc field + * @tty: tty structure + * @num: line discipline number + * + * This is probably overkill for real world processors but + * they are not on hot paths so a little discipline won't do + * any harm. + * + * Locking: takes termios_mutex + */ + +static void tty_set_termios_ldisc(struct tty_struct *tty, int num) +{ + mutex_lock(&tty->termios_mutex); + tty->termios->c_line = num; + mutex_unlock(&tty->termios_mutex); +} + + +/** + * tty_ldisc_restore - helper for tty ldisc change + * @tty: tty to recover + * @old: previous ldisc + * + * Restore the previous line discipline or N_TTY when a line discipline + * change fails due to an open error + */ + +static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) +{ + char buf[64]; + struct tty_ldisc new_ldisc; + + /* There is an outstanding reference here so this is safe */ + tty_ldisc_get(old->ops->num, old); + tty_ldisc_assign(tty, old); + tty_set_termios_ldisc(tty, old->ops->num); + if (old->ops->open && (old->ops->open(tty) < 0)) { + tty_ldisc_put(old->ops); + /* This driver is always present */ + if (tty_ldisc_get(N_TTY, &new_ldisc) < 0) + panic("n_tty: get"); + tty_ldisc_assign(tty, &new_ldisc); + tty_set_termios_ldisc(tty, N_TTY); + if (new_ldisc.ops->open) { + int r = new_ldisc.ops->open(tty); + if (r < 0) + panic("Couldn't open N_TTY ldisc for " + "%s --- error %d.", + tty_name(tty, buf), r); + } + } +} + +/** + * tty_set_ldisc - set line discipline + * @tty: the terminal to set + * @ldisc: the line discipline + * + * Set the discipline of a tty line. Must be called from a process + * context. + * + * Locking: takes tty_ldisc_lock. + * called functions take termios_mutex + */ + +int tty_set_ldisc(struct tty_struct *tty, int ldisc) +{ + int retval; + struct tty_ldisc o_ldisc, new_ldisc; + int work; + unsigned long flags; + struct tty_struct *o_tty; + +restart: + /* This is a bit ugly for now but means we can break the 'ldisc + is part of the tty struct' assumption later */ + retval = tty_ldisc_get(ldisc, &new_ldisc); + if (retval) + return retval; + + /* + * Problem: What do we do if this blocks ? + */ + + tty_wait_until_sent(tty, 0); + + if (tty->ldisc.ops->num == ldisc) { + tty_ldisc_put(new_ldisc.ops); + return 0; + } + + /* + * No more input please, we are switching. The new ldisc + * will update this value in the ldisc open function + */ + + tty->receive_room = 0; + + o_ldisc = tty->ldisc; + o_tty = tty->link; + + /* + * Make sure we don't change while someone holds a + * reference to the line discipline. The TTY_LDISC bit + * prevents anyone taking a reference once it is clear. + * We need the lock to avoid racing reference takers. + */ + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) { + if (tty->ldisc.refcount) { + /* Free the new ldisc we grabbed. Must drop the lock + first. */ + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + tty_ldisc_put(o_ldisc.ops); + /* + * There are several reasons we may be busy, including + * random momentary I/O traffic. We must therefore + * retry. We could distinguish between blocking ops + * and retries if we made tty_ldisc_wait() smarter. + * That is up for discussion. + */ + if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0) + return -ERESTARTSYS; + goto restart; + } + if (o_tty && o_tty->ldisc.refcount) { + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + tty_ldisc_put(o_tty->ldisc.ops); + if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0) + return -ERESTARTSYS; + goto restart; + } + } + /* + * If the TTY_LDISC bit is set, then we are racing against + * another ldisc change + */ + if (!test_bit(TTY_LDISC, &tty->flags)) { + struct tty_ldisc *ld; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + tty_ldisc_put(new_ldisc.ops); + ld = tty_ldisc_ref_wait(tty); + tty_ldisc_deref(ld); + goto restart; + } + + clear_bit(TTY_LDISC, &tty->flags); + if (o_tty) + clear_bit(TTY_LDISC, &o_tty->flags); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + + /* + * From this point on we know nobody has an ldisc + * usage reference, nor can they obtain one until + * we say so later on. + */ + + work = cancel_delayed_work(&tty->buf.work); + /* + * Wait for ->hangup_work and ->buf.work handlers to terminate + * MUST NOT hold locks here. + */ + flush_scheduled_work(); + /* Shutdown the current discipline. */ + if (o_ldisc.ops->close) + (o_ldisc.ops->close)(tty); + + /* Now set up the new line discipline. */ + tty_ldisc_assign(tty, &new_ldisc); + tty_set_termios_ldisc(tty, ldisc); + if (new_ldisc.ops->open) + retval = (new_ldisc.ops->open)(tty); + if (retval < 0) { + tty_ldisc_put(new_ldisc.ops); + tty_ldisc_restore(tty, &o_ldisc); + } + /* At this point we hold a reference to the new ldisc and a + a reference to the old ldisc. If we ended up flipping back + to the existing ldisc we have two references to it */ + + if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc) + tty->ops->set_ldisc(tty); + + tty_ldisc_put(o_ldisc.ops); + + /* + * Allow ldisc referencing to occur as soon as the driver + * ldisc callback completes. + */ + + tty_ldisc_enable(tty); + if (o_tty) + tty_ldisc_enable(o_tty); + + /* Restart it in case no characters kick it off. Safe if + already running */ + if (work) + schedule_delayed_work(&tty->buf.work, 1); + return retval; +} + + +/** + * tty_ldisc_setup - open line discipline + * @tty: tty being shut down + * @o_tty: pair tty for pty/tty pairs + * + * Called during the initial open of a tty/pty pair in order to set up the + * line discplines and bind them to the tty. + */ + +int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) +{ + struct tty_ldisc *ld = &tty->ldisc; + int retval; + + if (ld->ops->open) { + retval = (ld->ops->open)(tty); + if (retval) + return retval; + } + if (o_tty && o_tty->ldisc.ops->open) { + retval = (o_tty->ldisc.ops->open)(o_tty); + if (retval) { + if (ld->ops->close) + (ld->ops->close)(tty); + return retval; + } + tty_ldisc_enable(o_tty); + } + tty_ldisc_enable(tty); + return 0; +} + +/** + * tty_ldisc_release - release line discipline + * @tty: tty being shut down + * @o_tty: pair tty for pty/tty pairs + * + * Called during the final close of a tty/pty pair in order to shut down the + * line discpline layer. + */ + +void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) +{ + unsigned long flags; + struct tty_ldisc ld; + /* + * Prevent flush_to_ldisc() from rescheduling the work for later. Then + * kill any delayed work. As this is the final close it does not + * race with the set_ldisc code path. + */ + clear_bit(TTY_LDISC, &tty->flags); + cancel_delayed_work(&tty->buf.work); + + /* + * Wait for ->hangup_work and ->buf.work handlers to terminate + */ + + flush_scheduled_work(); + + /* + * Wait for any short term users (we know they are just driver + * side waiters as the file is closing so user count on the file + * side is zero. + */ + spin_lock_irqsave(&tty_ldisc_lock, flags); + while (tty->ldisc.refcount) { + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0); + spin_lock_irqsave(&tty_ldisc_lock, flags); + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + /* + * Shutdown the current line discipline, and reset it to N_TTY. + * + * FIXME: this MUST get fixed for the new reflocking + */ + if (tty->ldisc.ops->close) + (tty->ldisc.ops->close)(tty); + tty_ldisc_put(tty->ldisc.ops); + + /* + * Switch the line discipline back + */ + WARN_ON(tty_ldisc_get(N_TTY, &ld)); + tty_ldisc_assign(tty, &ld); + tty_set_termios_ldisc(tty, N_TTY); + if (o_tty) { + /* FIXME: could o_tty be in setldisc here ? */ + clear_bit(TTY_LDISC, &o_tty->flags); + if (o_tty->ldisc.ops->close) + (o_tty->ldisc.ops->close)(o_tty); + tty_ldisc_put(o_tty->ldisc.ops); + WARN_ON(tty_ldisc_get(N_TTY, &ld)); + tty_ldisc_assign(o_tty, &ld); + tty_set_termios_ldisc(o_tty, N_TTY); + } +} + +/** + * tty_ldisc_init - ldisc setup for new tty + * @tty: tty being allocated + * + * Set up the line discipline objects for a newly allocated tty. Note that + * the tty structure is not completely set up when this call is made. + */ + +void tty_ldisc_init(struct tty_struct *tty) +{ + struct tty_ldisc ld; + if (tty_ldisc_get(N_TTY, &ld) < 0) + panic("n_tty: init_tty"); + tty_ldisc_assign(tty, &ld); +} + +void tty_ldisc_begin(void) +{ + /* Setup the default TTY line discipline. */ + (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); +} diff --git a/include/linux/tty.h b/include/linux/tty.h index 4e5833073aa6..e3579cb086e0 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -317,8 +317,6 @@ extern void tty_wait_until_sent(struct tty_struct *tty, long timeout); extern int tty_check_change(struct tty_struct *tty); extern void stop_tty(struct tty_struct *tty); extern void start_tty(struct tty_struct *tty); -extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); -extern int tty_unregister_ldisc(int disc); extern int tty_register_driver(struct tty_driver *driver); extern int tty_unregister_driver(struct tty_driver *driver); extern struct device *tty_register_device(struct tty_driver *driver, @@ -383,6 +381,15 @@ extern void tty_port_init(struct tty_port *port); extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); +extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); +extern int tty_unregister_ldisc(int disc); +extern int tty_set_ldisc(struct tty_struct *tty, int ldisc); +extern int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty); +extern void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty); +extern void tty_ldisc_init(struct tty_struct *tty); +extern void tty_ldisc_begin(void); +/* This last one is just for the tty layer internals and shouldn't be used elsewhere */ +extern void tty_ldisc_enable(struct tty_struct *tty); /* n_tty.c */ -- cgit From 9e98966c7bb94355689478bc84cc3e0c190f977e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 22 Jul 2008 11:18:03 +0100 Subject: tty: rework break handling Some hardware needs to do break handling itself and may have partial support only. Make break_ctl return an error code. Add a tty driver flag so you can indicate driver hardware side break support. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/Kconfig | 2 +- drivers/char/amiserial.c | 3 +- drivers/char/cyclades.c | 8 ++--- drivers/char/esp.c | 5 +-- drivers/char/istallion.c | 11 +++--- drivers/char/moxa.c | 3 +- drivers/char/mxser.c | 3 +- drivers/char/pcmcia/synclink_cs.c | 5 +-- drivers/char/rocket.c | 5 +-- drivers/char/sx.c | 3 +- drivers/char/synclink.c | 7 ++-- drivers/char/synclink_gt.c | 9 ++--- drivers/char/synclinkmp.c | 9 ++--- drivers/char/tty_io.c | 71 +++++++++++++++------------------------ drivers/char/vme_scc.c | 5 +-- drivers/isdn/capi/capi.c | 3 +- drivers/serial/serial_core.c | 3 +- drivers/usb/class/cdc-acm.c | 9 +++-- drivers/usb/serial/usb-serial.c | 3 +- include/linux/tty_driver.h | 14 ++++++-- 20 files changed, 97 insertions(+), 84 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index ba8782b9c217..a185263b5862 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -218,7 +218,7 @@ config MOXA_SMARTIO config ISI tristate "Multi-Tech multiport card support (EXPERIMENTAL)" - depends on SERIAL_NONSTANDARD && PCI + depends on SERIAL_NONSTANDARD && PCI && BROKEN select FW_LOADER help This is a driver for the Multi-Tech cards which provide several diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 37457e5a4f2b..3530ff417a51 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -1248,7 +1248,7 @@ static int rs_tiocmset(struct tty_struct *tty, struct file *file, /* * rs_break() --- routine which turns the break handling on or off */ -static void rs_break(struct tty_struct *tty, int break_state) +static int rs_break(struct tty_struct *tty, int break_state) { struct async_struct * info = (struct async_struct *)tty->driver_data; unsigned long flags; @@ -1263,6 +1263,7 @@ static void rs_break(struct tty_struct *tty, int break_state) custom.adkcon = AC_UARTBRK; mb(); local_irq_restore(flags); + return 0; } diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index e991dc85f2fb..fe6d774fe2e4 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -3700,14 +3700,15 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, /* * cy_break() --- routine which turns the break handling on or off */ -static void cy_break(struct tty_struct *tty, int break_state) +static int cy_break(struct tty_struct *tty, int break_state) { struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; unsigned long flags; + int retval = 0; if (serial_paranoia_check(info, tty->name, "cy_break")) - return; + return -EINVAL; card = info->card; @@ -3736,8 +3737,6 @@ static void cy_break(struct tty_struct *tty, int break_state) } } } else { - int retval; - if (break_state == -1) { retval = cyz_issue_cmd(card, info->line - card->first_line, @@ -3758,6 +3757,7 @@ static void cy_break(struct tty_struct *tty, int break_state) } } spin_unlock_irqrestore(&card->card_lock, flags); + return retval; } /* cy_break */ static int get_mon_info(struct cyclades_port *info, diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 2eaf09f93e3d..7f077c0097f6 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -1725,13 +1725,13 @@ static int esp_tiocmset(struct tty_struct *tty, struct file *file, /* * rs_break() --- routine which turns the break handling on or off */ -static void esp_break(struct tty_struct *tty, int break_state) +static int esp_break(struct tty_struct *tty, int break_state) { struct esp_struct *info = tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "esp_break")) - return; + return -EINVAL; if (break_state == -1) { spin_lock_irqsave(&info->lock, flags); @@ -1747,6 +1747,7 @@ static void esp_break(struct tty_struct *tty, int break_state) serial_out(info, UART_ESI_CMD2, 0x00); spin_unlock_irqrestore(&info->lock, flags); } + return 0; } static int rs_ioctl(struct tty_struct *tty, struct file *file, diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 7930fba4bafc..63d22b5ebc0d 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -609,7 +609,7 @@ static void stli_unthrottle(struct tty_struct *tty); static void stli_stop(struct tty_struct *tty); static void stli_start(struct tty_struct *tty); static void stli_flushbuffer(struct tty_struct *tty); -static void stli_breakctl(struct tty_struct *tty, int state); +static int stli_breakctl(struct tty_struct *tty, int state); static void stli_waituntilsent(struct tty_struct *tty, int timeout); static void stli_sendxchar(struct tty_struct *tty, char ch); static void stli_hangup(struct tty_struct *tty); @@ -1909,7 +1909,7 @@ static void stli_flushbuffer(struct tty_struct *tty) /*****************************************************************************/ -static void stli_breakctl(struct tty_struct *tty, int state) +static int stli_breakctl(struct tty_struct *tty, int state) { struct stlibrd *brdp; struct stliport *portp; @@ -1917,15 +1917,16 @@ static void stli_breakctl(struct tty_struct *tty, int state) portp = tty->driver_data; if (portp == NULL) - return; + return -EINVAL; if (portp->brdnr >= stli_nrbrds) - return; + return -EINVAL; brdp = stli_brds[portp->brdnr]; if (brdp == NULL) - return; + return -EINVAL; arg = (state == -1) ? BREAKON : BREAKOFF; stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0); + return 0; } /*****************************************************************************/ diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 2bba250ffc8e..d3d7864e0c1e 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -374,12 +374,13 @@ copy: return ret; } -static void moxa_break_ctl(struct tty_struct *tty, int state) +static int moxa_break_ctl(struct tty_struct *tty, int state) { struct moxa_port *port = tty->driver_data; moxafunc(port->tableAddr, state ? FC_SendBreak : FC_StopBreak, Magic_code); + return 0; } static const struct tty_operations moxa_ops = { diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 1fb25571bf85..f04c3c58a05a 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -2183,7 +2183,7 @@ static void mxser_hangup(struct tty_struct *tty) /* * mxser_rs_break() --- routine which turns the break handling on or off */ -static void mxser_rs_break(struct tty_struct *tty, int break_state) +static int mxser_rs_break(struct tty_struct *tty, int break_state) { struct mxser_port *info = tty->driver_data; unsigned long flags; @@ -2196,6 +2196,7 @@ static void mxser_rs_break(struct tty_struct *tty, int break_state) outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC, info->ioaddr + UART_LCR); spin_unlock_irqrestore(&info->slock, flags); + return 0; } static void mxser_receive_chars(struct mxser_port *port, int *status) diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index b694d430f10e..d1fceabe3aef 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2230,7 +2230,7 @@ static int tiocmset(struct tty_struct *tty, struct file *file, * Arguments: tty pointer to tty instance data * break_state -1=set break condition, 0=clear */ -static void mgslpc_break(struct tty_struct *tty, int break_state) +static int mgslpc_break(struct tty_struct *tty, int break_state) { MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; unsigned long flags; @@ -2240,7 +2240,7 @@ static void mgslpc_break(struct tty_struct *tty, int break_state) __FILE__,__LINE__, info->device_name, break_state); if (mgslpc_paranoia_check(info, tty->name, "mgslpc_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->lock,flags); if (break_state == -1) @@ -2248,6 +2248,7 @@ static void mgslpc_break(struct tty_struct *tty, int break_state) else clear_reg_bits(info, CHA+DAFO, BIT6); spin_unlock_irqrestore(&info->lock,flags); + return 0; } /* Service an IOCTL request diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index e670eae2f510..584d791e84a6 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1236,13 +1236,13 @@ static void rp_set_termios(struct tty_struct *tty, } } -static void rp_break(struct tty_struct *tty, int break_state) +static int rp_break(struct tty_struct *tty, int break_state) { struct r_port *info = (struct r_port *) tty->driver_data; unsigned long flags; if (rocket_paranoia_check(info, "rp_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->slock, flags); if (break_state == -1) @@ -1250,6 +1250,7 @@ static void rp_break(struct tty_struct *tty, int break_state) else sClrBreak(&info->channel); spin_unlock_irqrestore(&info->slock, flags); + return 0; } /* diff --git a/drivers/char/sx.c b/drivers/char/sx.c index d5cffcd6a572..2162439bbe48 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1840,7 +1840,7 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp, return rc; } -static void sx_break(struct tty_struct *tty, int flag) +static int sx_break(struct tty_struct *tty, int flag) { struct sx_port *port = tty->driver_data; int rv; @@ -1857,6 +1857,7 @@ static void sx_break(struct tty_struct *tty, int flag) read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat))); unlock_kernel(); func_exit(); + return 0; } static int sx_tiocmget(struct tty_struct *tty, struct file *file) diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 527d220aa4aa..ef6706f09061 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -2897,9 +2897,9 @@ static int tiocmset(struct tty_struct *tty, struct file *file, * * Arguments: tty pointer to tty instance data * break_state -1=set break condition, 0=clear - * Return Value: None + * Return Value: error code */ -static void mgsl_break(struct tty_struct *tty, int break_state) +static int mgsl_break(struct tty_struct *tty, int break_state) { struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; unsigned long flags; @@ -2909,7 +2909,7 @@ static void mgsl_break(struct tty_struct *tty, int break_state) __FILE__,__LINE__, info->device_name, break_state); if (mgsl_paranoia_check(info, tty->name, "mgsl_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->irq_spinlock,flags); if (break_state == -1) @@ -2917,6 +2917,7 @@ static void mgsl_break(struct tty_struct *tty, int break_state) else usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7)); spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; } /* end of mgsl_break() */ diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 2c3e43bb2cc9..cf87bb89a77d 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -165,7 +165,7 @@ static int read_proc(char *page, char **start, off_t off, int count,int *eof, v static int chars_in_buffer(struct tty_struct *tty); static void throttle(struct tty_struct * tty); static void unthrottle(struct tty_struct * tty); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); /* * generic HDLC support and callbacks @@ -513,7 +513,7 @@ static int wait_mgsl_event(struct slgt_info *info, int __user *mask_ptr); static int tiocmget(struct tty_struct *tty, struct file *file); static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); static int get_interface(struct slgt_info *info, int __user *if_mode); static int set_interface(struct slgt_info *info, int if_mode); static int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); @@ -1452,14 +1452,14 @@ static void unthrottle(struct tty_struct * tty) * set or clear transmit break condition * break_state -1=set break condition, 0=clear */ -static void set_break(struct tty_struct *tty, int break_state) +static int set_break(struct tty_struct *tty, int break_state) { struct slgt_info *info = tty->driver_data; unsigned short value; unsigned long flags; if (sanity_check(info, tty->name, "set_break")) - return; + return -EINVAL; DBGINFO(("%s set_break(%d)\n", info->device_name, break_state)); spin_lock_irqsave(&info->lock,flags); @@ -1470,6 +1470,7 @@ static void set_break(struct tty_struct *tty, int break_state) value &= ~BIT6; wr_reg16(info, TCR, value); spin_unlock_irqrestore(&info->lock,flags); + return 0; } #if SYNCLINK_GENERIC_HDLC diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 5768c4136342..c0490cbd0db2 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -527,7 +527,7 @@ static int read_proc(char *page, char **start, off_t off, int count,int *eof, v static int chars_in_buffer(struct tty_struct *tty); static void throttle(struct tty_struct * tty); static void unthrottle(struct tty_struct * tty); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); #if SYNCLINK_GENERIC_HDLC #define dev_to_port(D) (dev_to_hdlc(D)->priv) @@ -552,7 +552,7 @@ static int wait_mgsl_event(SLMP_INFO *info, int __user *mask_ptr); static int tiocmget(struct tty_struct *tty, struct file *file); static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); -static void set_break(struct tty_struct *tty, int break_state); +static int set_break(struct tty_struct *tty, int break_state); static void add_device(SLMP_INFO *info); static void device_init(int adapter_num, struct pci_dev *pdev); @@ -1587,7 +1587,7 @@ static void unthrottle(struct tty_struct * tty) /* set or clear transmit break condition * break_state -1=set break condition, 0=clear */ -static void set_break(struct tty_struct *tty, int break_state) +static int set_break(struct tty_struct *tty, int break_state) { unsigned char RegValue; SLMP_INFO * info = (SLMP_INFO *)tty->driver_data; @@ -1598,7 +1598,7 @@ static void set_break(struct tty_struct *tty, int break_state) __FILE__,__LINE__, info->device_name, break_state); if (sanity_check(info, tty->name, "set_break")) - return; + return -EINVAL; spin_lock_irqsave(&info->lock,flags); RegValue = read_reg(info, CTL); @@ -1608,6 +1608,7 @@ static void set_break(struct tty_struct *tty, int break_state) RegValue &= ~BIT3; write_reg(info, CTL, RegValue); spin_unlock_irqrestore(&info->lock,flags); + return 0; } #if SYNCLINK_GENERIC_HDLC diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index d27a08b374d0..d94cd8410c53 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2849,16 +2849,29 @@ static int tiocsetd(struct tty_struct *tty, int __user *p) static int send_break(struct tty_struct *tty, unsigned int duration) { - if (tty_write_lock(tty, 0) < 0) - return -EINTR; - tty->ops->break_ctl(tty, -1); - if (!signal_pending(current)) - msleep_interruptible(duration); - tty->ops->break_ctl(tty, 0); - tty_write_unlock(tty); - if (signal_pending(current)) - return -EINTR; - return 0; + int retval; + + if (tty->ops->break_ctl == NULL) + return 0; + + if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK) + retval = tty->ops->break_ctl(tty, duration); + else { + /* Do the work ourselves */ + if (tty_write_lock(tty, 0) < 0) + return -EINTR; + retval = tty->ops->break_ctl(tty, -1); + if (retval) + goto out; + if (!signal_pending(current)) + msleep_interruptible(duration); + retval = tty->ops->break_ctl(tty, 0); +out: + tty_write_unlock(tty); + if (signal_pending(current)) + retval = -EINTR; + } + return retval; } /** @@ -2949,36 +2962,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) tty->driver->subtype == PTY_TYPE_MASTER) real_tty = tty->link; - /* - * Break handling by driver - */ - - retval = -EINVAL; - - if (!tty->ops->break_ctl) { - switch (cmd) { - case TIOCSBRK: - case TIOCCBRK: - if (tty->ops->ioctl) - retval = tty->ops->ioctl(tty, file, cmd, arg); - if (retval != -EINVAL && retval != -ENOIOCTLCMD) - printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name); - return retval; - - /* These two ioctl's always return success; even if */ - /* the driver doesn't support them. */ - case TCSBRK: - case TCSBRKP: - if (!tty->ops->ioctl) - return 0; - retval = tty->ops->ioctl(tty, file, cmd, arg); - if (retval != -EINVAL && retval != -ENOIOCTLCMD) - printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name); - if (retval == -ENOIOCTLCMD) - retval = 0; - return retval; - } - } /* * Factor out some common prep work @@ -3000,6 +2983,9 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } + /* + * Now do the stuff. + */ switch (cmd) { case TIOCSTI: return tiocsti(tty, p); @@ -3043,12 +3029,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) */ case TIOCSBRK: /* Turn break on, unconditionally */ if (tty->ops->break_ctl) - tty->ops->break_ctl(tty, -1); + return tty->ops->break_ctl(tty, -1); return 0; - case TIOCCBRK: /* Turn break off, unconditionally */ if (tty->ops->break_ctl) - tty->ops->break_ctl(tty, 0); + return tty->ops->break_ctl(tty, 0); return 0; case TCSBRK: /* SVID version: non-zero arg --> no break */ /* non-zero arg means wait for all output data diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c index f17ac043b551..69c5afe97f19 100644 --- a/drivers/char/vme_scc.c +++ b/drivers/char/vme_scc.c @@ -85,7 +85,7 @@ static irqreturn_t scc_rx_int(int irq, void *data); static irqreturn_t scc_stat_int(int irq, void *data); static irqreturn_t scc_spcond_int(int irq, void *data); static void scc_setsignals(struct scc_port *port, int dtr, int rts); -static void scc_break_ctl(struct tty_struct *tty, int break_state); +static int scc_break_ctl(struct tty_struct *tty, int break_state); static struct tty_driver *scc_driver; @@ -942,7 +942,7 @@ static int scc_ioctl(struct tty_struct *tty, struct file *file, } -static void scc_break_ctl(struct tty_struct *tty, int break_state) +static int scc_break_ctl(struct tty_struct *tty, int break_state) { struct scc_port *port = (struct scc_port *)tty->driver_data; unsigned long flags; @@ -952,6 +952,7 @@ static void scc_break_ctl(struct tty_struct *tty, int break_state) SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK, break_state ? TCR_SEND_BREAK : 0); local_irq_restore(flags); + return 0; } diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 8a35029caca0..19e005e81fef 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1302,11 +1302,12 @@ static void capinc_tty_hangup(struct tty_struct *tty) #endif } -static void capinc_tty_break_ctl(struct tty_struct *tty, int state) +static int capinc_tty_break_ctl(struct tty_struct *tty, int state) { #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state); #endif + return 0; } static void capinc_tty_flush_buffer(struct tty_struct *tty) diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 0bce1fe2c62a..f977c98cfa95 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -934,7 +934,7 @@ uart_tiocmset(struct tty_struct *tty, struct file *file, return ret; } -static void uart_break_ctl(struct tty_struct *tty, int break_state) +static int uart_break_ctl(struct tty_struct *tty, int break_state) { struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; @@ -945,6 +945,7 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state) port->ops->break_ctl(port, break_state); mutex_unlock(&state->mutex); + return 0; } static int uart_do_autoconfig(struct uart_state *state) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 95ae6377d7e5..0725b1871f23 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -732,13 +732,16 @@ static void acm_tty_unthrottle(struct tty_struct *tty) tasklet_schedule(&acm->urb_task); } -static void acm_tty_break_ctl(struct tty_struct *tty, int state) +static int acm_tty_break_ctl(struct tty_struct *tty, int state) { struct acm *acm = tty->driver_data; + int retval; if (!ACM_READY(acm)) - return; - if (acm_send_break(acm, state ? 0xffff : 0)) + return -EINVAL; + retval = acm_send_break(acm, state ? 0xffff : 0); + if (retval < 0) dbg("send break failed"); + return retval; } static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 51917b0f079a..8c2d531eedea 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -395,7 +395,7 @@ static void serial_set_termios(struct tty_struct *tty, struct ktermios *old) tty_termios_copy_hw(tty->termios, old); } -static void serial_break(struct tty_struct *tty, int break_state) +static int serial_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; @@ -409,6 +409,7 @@ static void serial_break(struct tty_struct *tty, int break_state) port->serial->type->break_ctl(tty, break_state); unlock_kernel(); } + return 0; } static int serial_read_proc(char *page, char **start, off_t off, int count, diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index d2a003586761..e1065ac0d922 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -135,7 +135,7 @@ * * Optional: * - * void (*break_ctl)(struct tty_stuct *tty, int state); + * int (*break_ctl)(struct tty_stuct *tty, int state); * * This optional routine requests the tty driver to turn on or * off BREAK status on the RS-232 port. If state is -1, @@ -146,6 +146,10 @@ * handle the following ioctls: TCSBRK, TCSBRKP, TIOCSBRK, * TIOCCBRK. * + * If the driver sets TTY_DRIVER_HARDWARE_BREAK then the interface + * will also be called with actual times and the hardware is expected + * to do the delay work itself. 0 and -1 are still used for on/off. + * * Optional: Required for TCSBRK/BRKP/etc handling. * * void (*wait_until_sent)(struct tty_struct *tty, int timeout); @@ -192,7 +196,7 @@ struct tty_operations { void (*stop)(struct tty_struct *tty); void (*start)(struct tty_struct *tty); void (*hangup)(struct tty_struct *tty); - void (*break_ctl)(struct tty_struct *tty, int state); + int (*break_ctl)(struct tty_struct *tty, int state); void (*flush_buffer)(struct tty_struct *tty); void (*set_ldisc)(struct tty_struct *tty); void (*wait_until_sent)(struct tty_struct *tty, int timeout); @@ -285,12 +289,18 @@ extern struct tty_driver *tty_find_polling_driver(char *name, int *line); * TTY_DRIVER_DEVPTS_MEM -- don't use the standard arrays, instead * use dynamic memory keyed through the devpts filesystem. This * is only applicable to the pty driver. + * + * TTY_DRIVER_HARDWARE_BREAK -- hardware handles break signals. Pass + * the requested timeout to the caller instead of using a simple + * on/off interface. + * */ #define TTY_DRIVER_INSTALLED 0x0001 #define TTY_DRIVER_RESET_TERMIOS 0x0002 #define TTY_DRIVER_REAL_RAW 0x0004 #define TTY_DRIVER_DYNAMIC_DEV 0x0008 #define TTY_DRIVER_DEVPTS_MEM 0x0010 +#define TTY_DRIVER_HARDWARE_BREAK 0x0020 /* tty driver types */ #define TTY_DRIVER_TYPE_SYSTEM 0x0001 -- cgit From e5590717afd5fb6f494323206a1a35ea25610c2d Mon Sep 17 00:00:00 2001 From: Paul Fulghum Date: Tue, 22 Jul 2008 11:21:39 +0100 Subject: synclink_gt: add serial bit order control Add control of hardware serial bit order between LSB first (default/standard) and MSB first. Signed-off-by: Paul Fulghum Signed-off-by: Andrew Morton Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/synclink_gt.c | 2 ++ include/linux/synclink.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include/linux') diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 0e59cf54adaf..3cfc9e1f8882 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -4403,6 +4403,8 @@ static void msc_set_vcr(struct slgt_info *info) break; } + if (info->if_mode & MGSL_INTERFACE_MSB_FIRST) + val |= BIT4; if (info->signals & SerialSignal_DTR) val |= BIT3; if (info->signals & SerialSignal_RTS) diff --git a/include/linux/synclink.h b/include/linux/synclink.h index 45f6bc82d317..c844a229acc9 100644 --- a/include/linux/synclink.h +++ b/include/linux/synclink.h @@ -136,6 +136,7 @@ #define MGSL_INTERFACE_RTS_EN 0x10 #define MGSL_INTERFACE_LL 0x20 #define MGSL_INTERFACE_RL 0x40 +#define MGSL_INTERFACE_MSB_FIRST 0x80 typedef struct _MGSL_PARAMS { -- cgit From 217d5a51953143046cf2972eebdefe2b2db05718 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 22 Jul 2008 17:43:42 +0200 Subject: fs_enet: Remove unused fields in the fs_mii_bb_platform_info structure. The mdio_port, mdio_bit, mdc_port and mdc_bit fields in the fs_mii_bb_platform_info structure are left-overs from the move to the Phy Abstraction Layer subsystem. They are not used anymore and can be safely removed. Signed-off-by: Laurent Pinchart Signed-off-by: Jeff Garzik --- include/linux/fs_enet_pd.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fs_enet_pd.h b/include/linux/fs_enet_pd.h index 9bc045b8c478..decc4b56ccf9 100644 --- a/include/linux/fs_enet_pd.h +++ b/include/linux/fs_enet_pd.h @@ -103,10 +103,6 @@ struct fs_mii_bb_platform_info { struct fs_mii_bit mdio_dir; struct fs_mii_bit mdio_dat; struct fs_mii_bit mdc_dat; - int mdio_port; /* port & bit for MDIO */ - int mdio_bit; - int mdc_port; /* port & bit for MDC */ - int mdc_bit; int delay; /* delay in us */ int irq[32]; /* irqs per phy's */ }; -- cgit From d29f749e252bcdbfe7a75a58f0ee92da16f127c0 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Tue, 22 Jul 2008 14:09:06 -0700 Subject: net: Fix build failure with 'make mandocs'. The function header comments have to go with the functions they are documenting, or things go horribly wrong when we try to process them with the docbook tools. Warning(include/linux/netdevice.h:1006): No description found for parameter 'dev_queue' Warning(include/linux/netdevice.h:1033): No description found for parameter 'dev_queue' Warning(include/linux/netdevice.h:1067): No description found for parameter 'dev_queue' Warning(include/linux/netdevice.h:1093): No description found for parameter 'dev_queue' Warning(include/linux/netdevice.h:1474): No description found for parameter 'txq' Error(net/core/dev.c:1674): cannot understand prototype: 'u32 simple_tx_hashrnd; ' Signed-off-by: Dave Jones Acked-by: Randy Dunlap Signed-off-by: David S. Miller --- include/linux/netdevice.h | 58 +++++++++++++++++++++++------------------------ net/core/dev.c | 51 ++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f5ea445f89f0..b4d056ceab96 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -996,17 +996,17 @@ static inline void netif_tx_schedule_all(struct net_device *dev) netif_schedule_queue(netdev_get_tx_queue(dev, i)); } +static inline void netif_tx_start_queue(struct netdev_queue *dev_queue) +{ + clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + /** * netif_start_queue - allow transmit * @dev: network device * * Allow upper layers to call the device hard_start_xmit routine. */ -static inline void netif_tx_start_queue(struct netdev_queue *dev_queue) -{ - clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state); -} - static inline void netif_start_queue(struct net_device *dev) { netif_tx_start_queue(netdev_get_tx_queue(dev, 0)); @@ -1022,13 +1022,6 @@ static inline void netif_tx_start_all_queues(struct net_device *dev) } } -/** - * netif_wake_queue - restart transmit - * @dev: network device - * - * Allow upper layers to call the device hard_start_xmit routine. - * Used for flow control when transmit resources are available. - */ static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue) { #ifdef CONFIG_NETPOLL_TRAP @@ -1041,6 +1034,13 @@ static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue) __netif_schedule(dev_queue->qdisc); } +/** + * netif_wake_queue - restart transmit + * @dev: network device + * + * Allow upper layers to call the device hard_start_xmit routine. + * Used for flow control when transmit resources are available. + */ static inline void netif_wake_queue(struct net_device *dev) { netif_tx_wake_queue(netdev_get_tx_queue(dev, 0)); @@ -1056,6 +1056,11 @@ static inline void netif_tx_wake_all_queues(struct net_device *dev) } } +static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) +{ + set_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + /** * netif_stop_queue - stop transmitted packets * @dev: network device @@ -1063,11 +1068,6 @@ static inline void netif_tx_wake_all_queues(struct net_device *dev) * Stop upper layers calling the device hard_start_xmit routine. * Used for flow control when transmit resources are unavailable. */ -static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) -{ - set_bit(__QUEUE_STATE_XOFF, &dev_queue->state); -} - static inline void netif_stop_queue(struct net_device *dev) { netif_tx_stop_queue(netdev_get_tx_queue(dev, 0)); @@ -1083,17 +1083,17 @@ static inline void netif_tx_stop_all_queues(struct net_device *dev) } } +static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) +{ + return test_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + /** * netif_queue_stopped - test if transmit queue is flowblocked * @dev: network device * * Test if transmit queue on device is currently unable to send. */ -static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) -{ - return test_bit(__QUEUE_STATE_XOFF, &dev_queue->state); -} - static inline int netif_queue_stopped(const struct net_device *dev) { return netif_tx_queue_stopped(netdev_get_tx_queue(dev, 0)); @@ -1463,13 +1463,6 @@ static inline void netif_rx_complete(struct net_device *dev, local_irq_restore(flags); } -/** - * netif_tx_lock - grab network device transmit lock - * @dev: network device - * @cpu: cpu number of lock owner - * - * Get network device transmit lock - */ static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu) { spin_lock(&txq->_xmit_lock); @@ -1482,6 +1475,13 @@ static inline void __netif_tx_lock_bh(struct netdev_queue *txq) txq->xmit_lock_owner = smp_processor_id(); } +/** + * netif_tx_lock - grab network device transmit lock + * @dev: network device + * @cpu: cpu number of lock owner + * + * Get network device transmit lock + */ static inline void netif_tx_lock(struct net_device *dev) { int cpu = smp_processor_id(); diff --git a/net/core/dev.c b/net/core/dev.c index ad5598d2bb37..65eea83613ef 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1645,32 +1645,6 @@ out_kfree_skb: return 0; } -/** - * dev_queue_xmit - transmit a buffer - * @skb: buffer to transmit - * - * Queue a buffer for transmission to a network device. The caller must - * have set the device and priority and built the buffer before calling - * this function. The function can be called from an interrupt. - * - * A negative errno code is returned on a failure. A success does not - * guarantee the frame will be transmitted as it may be dropped due - * to congestion or traffic shaping. - * - * ----------------------------------------------------------------------------------- - * I notice this method can also return errors from the queue disciplines, - * including NET_XMIT_DROP, which is a positive value. So, errors can also - * be positive. - * - * Regardless of the return value, the skb is consumed, so it is currently - * difficult to retry a send to this method. (You can bump the ref count - * before sending to hold a reference for retry if you are careful.) - * - * When calling this method, interrupts MUST be enabled. This is because - * the BH enable code must have IRQs enabled so that it will not deadlock. - * --BLG - */ - static u32 simple_tx_hashrnd; static int simple_tx_hashrnd_initialized = 0; @@ -1738,6 +1712,31 @@ static struct netdev_queue *dev_pick_tx(struct net_device *dev, return netdev_get_tx_queue(dev, queue_index); } +/** + * dev_queue_xmit - transmit a buffer + * @skb: buffer to transmit + * + * Queue a buffer for transmission to a network device. The caller must + * have set the device and priority and built the buffer before calling + * this function. The function can be called from an interrupt. + * + * A negative errno code is returned on a failure. A success does not + * guarantee the frame will be transmitted as it may be dropped due + * to congestion or traffic shaping. + * + * ----------------------------------------------------------------------------------- + * I notice this method can also return errors from the queue disciplines, + * including NET_XMIT_DROP, which is a positive value. So, errors can also + * be positive. + * + * Regardless of the return value, the skb is consumed, so it is currently + * difficult to retry a send to this method. (You can bump the ref count + * before sending to hold a reference for retry if you are careful.) + * + * When calling this method, interrupts MUST be enabled. This is because + * the BH enable code must have IRQs enabled so that it will not deadlock. + * --BLG + */ int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; -- cgit From 8086cd451f08f4c0f9693fc66d87754bbd18cfba Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 22 Jul 2008 14:19:19 -0700 Subject: netns: make get_proc_net() static get_proc_net() can now become static. Signed-off-by: Adrian Bunk Acked-by: Pavel Emelyanov Signed-off-by: David S. Miller --- fs/proc/proc_net.c | 11 +++++------ include/linux/proc_fs.h | 2 -- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index b224a28e0c15..7bc296f424ae 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -27,6 +27,11 @@ #include "internal.h" +static struct net *get_proc_net(const struct inode *inode) +{ + return maybe_get_net(PDE_NET(PDE(inode))); +} + int seq_open_net(struct inode *ino, struct file *f, const struct seq_operations *ops, int size) { @@ -185,12 +190,6 @@ void proc_net_remove(struct net *net, const char *name) } EXPORT_SYMBOL_GPL(proc_net_remove); -struct net *get_proc_net(const struct inode *inode) -{ - return maybe_get_net(PDE_NET(PDE(inode))); -} -EXPORT_SYMBOL_GPL(get_proc_net); - static __net_init int proc_net_ns_init(struct net *net) { struct proc_dir_entry *netd, *net_statd; diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index fff1d27ddb4c..15a9eaf4a802 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -305,8 +305,6 @@ static inline struct net *PDE_NET(struct proc_dir_entry *pde) return pde->parent->data; } -struct net *get_proc_net(const struct inode *inode); - struct proc_maps_private { struct pid *pid; struct task_struct *task; -- cgit From 47b374752aed1c029f995473c7c463ee3ae5fbaa Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 22 Jul 2008 14:19:39 -0700 Subject: IB/mlx4: Rename struct mlx4_lso_seg to mlx4_wqe_lso_seg Make the struct name consistent with other WQE segment struct types defined in . Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/qp.c | 2 +- include/linux/mlx4/qp.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 89eb6cbe592e..bda0859a5ac5 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1395,7 +1395,7 @@ static void __set_data_seg(struct mlx4_wqe_data_seg *dseg, struct ib_sge *sg) dseg->addr = cpu_to_be64(sg->addr); } -static int build_lso_seg(struct mlx4_lso_seg *wqe, struct ib_send_wr *wr, +static int build_lso_seg(struct mlx4_wqe_lso_seg *wqe, struct ib_send_wr *wr, struct mlx4_ib_qp *qp, unsigned *lso_seg_len) { unsigned halign = ALIGN(sizeof *wqe + wr->wr.ud.hlen, 16); diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index 7f128b266faa..f02e9ed36cfa 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -219,7 +219,7 @@ struct mlx4_wqe_datagram_seg { __be32 reservd[2]; }; -struct mlx4_lso_seg { +struct mlx4_wqe_lso_seg { __be32 mss_hdr_size; __be32 header[0]; }; -- cgit From e5899e1b7d73e67de758a32174a859cc2586c0b9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 19 Jul 2008 14:39:24 +0200 Subject: PCI PM: make more PCI PM core functionality available to drivers Make more PCI PM core functionality available to drivers * Export pci_pme_capable() so that it can be called directly by drivers (for example, tg3 needs that). * Move the state choosing part of pci_prepare_to_sleep() to a separate function, pci_target_state(), that can be called directly by drivers (for example, tg3 needs that). Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 34 ++++++++++++++++++++++++---------- include/linux/pci.h | 2 ++ 2 files changed, 26 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d00f0e0d8453..e9c356236d27 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1040,7 +1040,7 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) * @dev: PCI device to handle. * @state: PCI state from which device will issue PME#. */ -static bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) +bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) { if (!dev->pm_cap) return false; @@ -1123,17 +1123,10 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) } /** - * pci_prepare_to_sleep - prepare PCI device for system-wide transition into a sleep state - * @dev: Device to handle. - * - * Choose the power state appropriate for the device depending on whether - * it can wake up the system and/or is power manageable by the platform - * (PCI_D3hot is the default) and put the device into that state. */ -int pci_prepare_to_sleep(struct pci_dev *dev) +pci_power_t pci_target_state(struct pci_dev *dev) { pci_power_t target_state = PCI_D3hot; - int error; if (platform_pci_power_manageable(dev)) { /* @@ -1160,7 +1153,7 @@ int pci_prepare_to_sleep(struct pci_dev *dev) * to generate PME#. */ if (!dev->pm_cap) - return -EIO; + return PCI_POWER_ERROR; if (dev->pme_support) { while (target_state @@ -1169,6 +1162,25 @@ int pci_prepare_to_sleep(struct pci_dev *dev) } } + return target_state; +} + +/** + * pci_prepare_to_sleep - prepare PCI device for system-wide transition into a sleep state + * @dev: Device to handle. + * + * Choose the power state appropriate for the device depending on whether + * it can wake up the system and/or is power manageable by the platform + * (PCI_D3hot is the default) and put the device into that state. + */ +int pci_prepare_to_sleep(struct pci_dev *dev) +{ + pci_power_t target_state = pci_target_state(dev); + int error; + + if (target_state == PCI_POWER_ERROR) + return -EIO; + pci_enable_wake(dev, target_state, true); error = pci_set_power_state(dev, target_state); @@ -1918,7 +1930,9 @@ EXPORT_SYMBOL(pci_select_bars); EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_save_state); EXPORT_SYMBOL(pci_restore_state); +EXPORT_SYMBOL(pci_pme_capable); EXPORT_SYMBOL(pci_enable_wake); +EXPORT_SYMBOL(pci_target_state); EXPORT_SYMBOL(pci_prepare_to_sleep); EXPORT_SYMBOL(pci_back_from_sleep); EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); diff --git a/include/linux/pci.h b/include/linux/pci.h index a6a088e1a804..1d296d31abe0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -638,7 +638,9 @@ int pci_save_state(struct pci_dev *dev); int pci_restore_state(struct pci_dev *dev); int pci_set_power_state(struct pci_dev *dev, pci_power_t state); pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); +bool pci_pme_capable(struct pci_dev *dev, pci_power_t state); int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable); +pci_power_t pci_target_state(struct pci_dev *dev); int pci_prepare_to_sleep(struct pci_dev *dev); int pci_back_from_sleep(struct pci_dev *dev); -- cgit From 7f1b358a236ee9c19657a619ac6f2dcabcaa0924 Mon Sep 17 00:00:00 2001 From: Maciej Sosnowski Date: Tue, 22 Jul 2008 17:30:57 -0700 Subject: I/OAT: I/OAT version 3.0 support This patch adds to ioatdma and dca modules support for Intel I/OAT DMA engine ver.3 (aka CB3 device). The main features of I/OAT ver.3 are: * 8 single channel DMA devices (8 channels total) * 8 DCA providers, each can accept 2 requesters * 8-bit TAG values and 32-bit extended APIC IDs Signed-off-by: Maciej Sosnowski Signed-off-by: Dan Williams --- drivers/dca/dca-core.c | 131 ++++++++++++++++----- drivers/dca/dca-sysfs.c | 3 +- drivers/dma/ioat.c | 15 +++ drivers/dma/ioat_dca.c | 244 ++++++++++++++++++++++++++++++++++++++-- drivers/dma/ioat_dma.c | 96 ++++++++++++++-- drivers/dma/ioatdma.h | 5 +- drivers/dma/ioatdma_hw.h | 1 + drivers/dma/ioatdma_registers.h | 20 ++++ include/linux/dca.h | 7 +- include/linux/pci_ids.h | 8 ++ 10 files changed, 481 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index bf5b92f86df7..ec249d2db633 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c @@ -28,13 +28,29 @@ #include #include -MODULE_LICENSE("GPL"); +#define DCA_VERSION "1.4" -/* For now we're assuming a single, global, DCA provider for the system. */ +MODULE_VERSION(DCA_VERSION); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Intel Corporation"); static DEFINE_SPINLOCK(dca_lock); -static struct dca_provider *global_dca = NULL; +static LIST_HEAD(dca_providers); + +static struct dca_provider *dca_find_provider_by_dev(struct device *dev) +{ + struct dca_provider *dca, *ret = NULL; + + list_for_each_entry(dca, &dca_providers, node) { + if ((!dev) || (dca->ops->dev_managed(dca, dev))) { + ret = dca; + break; + } + } + + return ret; +} /** * dca_add_requester - add a dca client to the list @@ -42,25 +58,39 @@ static struct dca_provider *global_dca = NULL; */ int dca_add_requester(struct device *dev) { - int err, slot; + struct dca_provider *dca; + int err, slot = -ENODEV; - if (!global_dca) - return -ENODEV; + if (!dev) + return -EFAULT; spin_lock(&dca_lock); - slot = global_dca->ops->add_requester(global_dca, dev); - spin_unlock(&dca_lock); - if (slot < 0) + + /* check if the requester has not been added already */ + dca = dca_find_provider_by_dev(dev); + if (dca) { + spin_unlock(&dca_lock); + return -EEXIST; + } + + list_for_each_entry(dca, &dca_providers, node) { + slot = dca->ops->add_requester(dca, dev); + if (slot >= 0) + break; + } + if (slot < 0) { + spin_unlock(&dca_lock); return slot; + } - err = dca_sysfs_add_req(global_dca, dev, slot); + err = dca_sysfs_add_req(dca, dev, slot); if (err) { - spin_lock(&dca_lock); - global_dca->ops->remove_requester(global_dca, dev); + dca->ops->remove_requester(dca, dev); spin_unlock(&dca_lock); return err; } + spin_unlock(&dca_lock); return 0; } EXPORT_SYMBOL_GPL(dca_add_requester); @@ -71,30 +101,78 @@ EXPORT_SYMBOL_GPL(dca_add_requester); */ int dca_remove_requester(struct device *dev) { + struct dca_provider *dca; int slot; - if (!global_dca) - return -ENODEV; + + if (!dev) + return -EFAULT; spin_lock(&dca_lock); - slot = global_dca->ops->remove_requester(global_dca, dev); - spin_unlock(&dca_lock); - if (slot < 0) + dca = dca_find_provider_by_dev(dev); + if (!dca) { + spin_unlock(&dca_lock); + return -ENODEV; + } + slot = dca->ops->remove_requester(dca, dev); + if (slot < 0) { + spin_unlock(&dca_lock); return slot; + } - dca_sysfs_remove_req(global_dca, slot); + dca_sysfs_remove_req(dca, slot); + + spin_unlock(&dca_lock); return 0; } EXPORT_SYMBOL_GPL(dca_remove_requester); /** - * dca_get_tag - return the dca tag for the given cpu + * dca_common_get_tag - return the dca tag (serves both new and old api) + * @dev - the device that wants dca service * @cpu - the cpuid as returned by get_cpu() */ -u8 dca_get_tag(int cpu) +u8 dca_common_get_tag(struct device *dev, int cpu) { - if (!global_dca) + struct dca_provider *dca; + u8 tag; + + spin_lock(&dca_lock); + + dca = dca_find_provider_by_dev(dev); + if (!dca) { + spin_unlock(&dca_lock); return -ENODEV; - return global_dca->ops->get_tag(global_dca, cpu); + } + tag = dca->ops->get_tag(dca, dev, cpu); + + spin_unlock(&dca_lock); + return tag; +} + +/** + * dca3_get_tag - return the dca tag to the requester device + * for the given cpu (new api) + * @dev - the device that wants dca service + * @cpu - the cpuid as returned by get_cpu() + */ +u8 dca3_get_tag(struct device *dev, int cpu) +{ + if (!dev) + return -EFAULT; + + return dca_common_get_tag(dev, cpu); +} +EXPORT_SYMBOL_GPL(dca3_get_tag); + +/** + * dca_get_tag - return the dca tag for the given cpu (old api) + * @cpu - the cpuid as returned by get_cpu() + */ +u8 dca_get_tag(int cpu) +{ + struct device *dev = NULL; + + return dca_common_get_tag(dev, cpu); } EXPORT_SYMBOL_GPL(dca_get_tag); @@ -140,12 +218,10 @@ int register_dca_provider(struct dca_provider *dca, struct device *dev) { int err; - if (global_dca) - return -EEXIST; err = dca_sysfs_add_provider(dca, dev); if (err) return err; - global_dca = dca; + list_add(&dca->node, &dca_providers); blocking_notifier_call_chain(&dca_provider_chain, DCA_PROVIDER_ADD, NULL); return 0; @@ -158,11 +234,9 @@ EXPORT_SYMBOL_GPL(register_dca_provider); */ void unregister_dca_provider(struct dca_provider *dca) { - if (!global_dca) - return; blocking_notifier_call_chain(&dca_provider_chain, DCA_PROVIDER_REMOVE, NULL); - global_dca = NULL; + list_del(&dca->node); dca_sysfs_remove_provider(dca); } EXPORT_SYMBOL_GPL(unregister_dca_provider); @@ -187,6 +261,7 @@ EXPORT_SYMBOL_GPL(dca_unregister_notify); static int __init dca_init(void) { + printk(KERN_ERR "dca service started, version %s\n", DCA_VERSION); return dca_sysfs_init(); } diff --git a/drivers/dca/dca-sysfs.c b/drivers/dca/dca-sysfs.c index 011328faa5f2..3d47e9d8e34f 100644 --- a/drivers/dca/dca-sysfs.c +++ b/drivers/dca/dca-sysfs.c @@ -13,9 +13,10 @@ static spinlock_t dca_idr_lock; int dca_sysfs_add_req(struct dca_provider *dca, struct device *dev, int slot) { struct device *cd; + static int req_count; cd = device_create(dca_class, dca->cd, MKDEV(0, slot + 1), - "requester%d", slot); + "requester%d", req_count++); if (IS_ERR(cd)) return PTR_ERR(cd); return 0; diff --git a/drivers/dma/ioat.c b/drivers/dma/ioat.c index 16e0fd8facfb..9b16a3af9a0a 100644 --- a/drivers/dma/ioat.c +++ b/drivers/dma/ioat.c @@ -47,6 +47,16 @@ static struct pci_device_id ioat_pci_tbl[] = { /* I/OAT v2 platforms */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB) }, + + /* I/OAT v3 platforms */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG3) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG4) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG5) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG6) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_TBG7) }, { 0, } }; @@ -83,6 +93,11 @@ static int ioat_setup_functionality(struct pci_dev *pdev, void __iomem *iobase) if (device->dma && ioat_dca_enabled) device->dca = ioat2_dca_init(pdev, iobase); break; + case IOAT_VER_3_0: + device->dma = ioat_dma_probe(pdev, iobase); + if (device->dma && ioat_dca_enabled) + device->dca = ioat3_dca_init(pdev, iobase); + break; default: err = -ENODEV; break; diff --git a/drivers/dma/ioat_dca.c b/drivers/dma/ioat_dca.c index 9e922760b7ff..6cf622da0286 100644 --- a/drivers/dma/ioat_dca.c +++ b/drivers/dma/ioat_dca.c @@ -37,12 +37,18 @@ #include "ioatdma_registers.h" /* - * Bit 16 of a tag map entry is the "valid" bit, if it is set then bits 0:15 + * Bit 7 of a tag map entry is the "valid" bit, if it is set then bits 0:6 * contain the bit number of the APIC ID to map into the DCA tag. If the valid * bit is not set, then the value must be 0 or 1 and defines the bit in the tag. */ #define DCA_TAG_MAP_VALID 0x80 +#define DCA3_TAG_MAP_BIT_TO_INV 0x80 +#define DCA3_TAG_MAP_BIT_TO_SEL 0x40 +#define DCA3_TAG_MAP_LITERAL_VAL 0x1 + +#define DCA_TAG_MAP_MASK 0xDF + /* * "Legacy" DCA systems do not implement the DCA register set in the * I/OAT device. Software needs direct support for their tag mappings. @@ -95,6 +101,7 @@ struct ioat_dca_slot { }; #define IOAT_DCA_MAX_REQ 6 +#define IOAT3_DCA_MAX_REQ 2 struct ioat_dca_priv { void __iomem *iobase; @@ -171,7 +178,9 @@ static int ioat_dca_remove_requester(struct dca_provider *dca, return -ENODEV; } -static u8 ioat_dca_get_tag(struct dca_provider *dca, int cpu) +static u8 ioat_dca_get_tag(struct dca_provider *dca, + struct device *dev, + int cpu) { struct ioat_dca_priv *ioatdca = dca_priv(dca); int i, apic_id, bit, value; @@ -193,10 +202,26 @@ static u8 ioat_dca_get_tag(struct dca_provider *dca, int cpu) return tag; } +static int ioat_dca_dev_managed(struct dca_provider *dca, + struct device *dev) +{ + struct ioat_dca_priv *ioatdca = dca_priv(dca); + struct pci_dev *pdev; + int i; + + pdev = to_pci_dev(dev); + for (i = 0; i < ioatdca->max_requesters; i++) { + if (ioatdca->req_slots[i].pdev == pdev) + return 1; + } + return 0; +} + static struct dca_ops ioat_dca_ops = { .add_requester = ioat_dca_add_requester, .remove_requester = ioat_dca_remove_requester, .get_tag = ioat_dca_get_tag, + .dev_managed = ioat_dca_dev_managed, }; @@ -207,6 +232,8 @@ struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase) u8 *tag_map = NULL; int i; int err; + u8 version; + u8 max_requesters; if (!system_has_dca_enabled(pdev)) return NULL; @@ -237,15 +264,20 @@ struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase) if (tag_map == NULL) return NULL; + version = readb(iobase + IOAT_VER_OFFSET); + if (version == IOAT_VER_3_0) + max_requesters = IOAT3_DCA_MAX_REQ; + else + max_requesters = IOAT_DCA_MAX_REQ; + dca = alloc_dca_provider(&ioat_dca_ops, sizeof(*ioatdca) + - (sizeof(struct ioat_dca_slot) * IOAT_DCA_MAX_REQ)); + (sizeof(struct ioat_dca_slot) * max_requesters)); if (!dca) return NULL; ioatdca = dca_priv(dca); - ioatdca->max_requesters = IOAT_DCA_MAX_REQ; - + ioatdca->max_requesters = max_requesters; ioatdca->dca_base = iobase + 0x54; /* copy over the APIC ID to DCA tag mapping */ @@ -323,11 +355,13 @@ static int ioat2_dca_remove_requester(struct dca_provider *dca, return -ENODEV; } -static u8 ioat2_dca_get_tag(struct dca_provider *dca, int cpu) +static u8 ioat2_dca_get_tag(struct dca_provider *dca, + struct device *dev, + int cpu) { u8 tag; - tag = ioat_dca_get_tag(dca, cpu); + tag = ioat_dca_get_tag(dca, dev, cpu); tag = (~tag) & 0x1F; return tag; } @@ -336,6 +370,7 @@ static struct dca_ops ioat2_dca_ops = { .add_requester = ioat2_dca_add_requester, .remove_requester = ioat2_dca_remove_requester, .get_tag = ioat2_dca_get_tag, + .dev_managed = ioat_dca_dev_managed, }; static int ioat2_dca_count_dca_slots(void __iomem *iobase, u16 dca_offset) @@ -425,3 +460,198 @@ struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase) return dca; } + +static int ioat3_dca_add_requester(struct dca_provider *dca, struct device *dev) +{ + struct ioat_dca_priv *ioatdca = dca_priv(dca); + struct pci_dev *pdev; + int i; + u16 id; + u16 global_req_table; + + /* This implementation only supports PCI-Express */ + if (dev->bus != &pci_bus_type) + return -ENODEV; + pdev = to_pci_dev(dev); + id = dcaid_from_pcidev(pdev); + + if (ioatdca->requester_count == ioatdca->max_requesters) + return -ENODEV; + + for (i = 0; i < ioatdca->max_requesters; i++) { + if (ioatdca->req_slots[i].pdev == NULL) { + /* found an empty slot */ + ioatdca->requester_count++; + ioatdca->req_slots[i].pdev = pdev; + ioatdca->req_slots[i].rid = id; + global_req_table = + readw(ioatdca->dca_base + IOAT3_DCA_GREQID_OFFSET); + writel(id | IOAT_DCA_GREQID_VALID, + ioatdca->iobase + global_req_table + (i * 4)); + return i; + } + } + /* Error, ioatdma->requester_count is out of whack */ + return -EFAULT; +} + +static int ioat3_dca_remove_requester(struct dca_provider *dca, + struct device *dev) +{ + struct ioat_dca_priv *ioatdca = dca_priv(dca); + struct pci_dev *pdev; + int i; + u16 global_req_table; + + /* This implementation only supports PCI-Express */ + if (dev->bus != &pci_bus_type) + return -ENODEV; + pdev = to_pci_dev(dev); + + for (i = 0; i < ioatdca->max_requesters; i++) { + if (ioatdca->req_slots[i].pdev == pdev) { + global_req_table = + readw(ioatdca->dca_base + IOAT3_DCA_GREQID_OFFSET); + writel(0, ioatdca->iobase + global_req_table + (i * 4)); + ioatdca->req_slots[i].pdev = NULL; + ioatdca->req_slots[i].rid = 0; + ioatdca->requester_count--; + return i; + } + } + return -ENODEV; +} + +static u8 ioat3_dca_get_tag(struct dca_provider *dca, + struct device *dev, + int cpu) +{ + u8 tag; + + struct ioat_dca_priv *ioatdca = dca_priv(dca); + int i, apic_id, bit, value; + u8 entry; + + tag = 0; + apic_id = cpu_physical_id(cpu); + + for (i = 0; i < IOAT_TAG_MAP_LEN; i++) { + entry = ioatdca->tag_map[i]; + if (entry & DCA3_TAG_MAP_BIT_TO_SEL) { + bit = entry & + ~(DCA3_TAG_MAP_BIT_TO_SEL | DCA3_TAG_MAP_BIT_TO_INV); + value = (apic_id & (1 << bit)) ? 1 : 0; + } else if (entry & DCA3_TAG_MAP_BIT_TO_INV) { + bit = entry & ~DCA3_TAG_MAP_BIT_TO_INV; + value = (apic_id & (1 << bit)) ? 0 : 1; + } else { + value = (entry & DCA3_TAG_MAP_LITERAL_VAL) ? 1 : 0; + } + tag |= (value << i); + } + + return tag; +} + +static struct dca_ops ioat3_dca_ops = { + .add_requester = ioat3_dca_add_requester, + .remove_requester = ioat3_dca_remove_requester, + .get_tag = ioat3_dca_get_tag, + .dev_managed = ioat_dca_dev_managed, +}; + +static int ioat3_dca_count_dca_slots(void *iobase, u16 dca_offset) +{ + int slots = 0; + u32 req; + u16 global_req_table; + + global_req_table = readw(iobase + dca_offset + IOAT3_DCA_GREQID_OFFSET); + if (global_req_table == 0) + return 0; + + do { + req = readl(iobase + global_req_table + (slots * sizeof(u32))); + slots++; + } while ((req & IOAT_DCA_GREQID_LASTID) == 0); + + return slots; +} + +struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase) +{ + struct dca_provider *dca; + struct ioat_dca_priv *ioatdca; + int slots; + int i; + int err; + u16 dca_offset; + u16 csi_fsb_control; + u16 pcie_control; + u8 bit; + + union { + u64 full; + struct { + u32 low; + u32 high; + }; + } tag_map; + + if (!system_has_dca_enabled(pdev)) + return NULL; + + dca_offset = readw(iobase + IOAT_DCAOFFSET_OFFSET); + if (dca_offset == 0) + return NULL; + + slots = ioat3_dca_count_dca_slots(iobase, dca_offset); + if (slots == 0) + return NULL; + + dca = alloc_dca_provider(&ioat3_dca_ops, + sizeof(*ioatdca) + + (sizeof(struct ioat_dca_slot) * slots)); + if (!dca) + return NULL; + + ioatdca = dca_priv(dca); + ioatdca->iobase = iobase; + ioatdca->dca_base = iobase + dca_offset; + ioatdca->max_requesters = slots; + + /* some bios might not know to turn these on */ + csi_fsb_control = readw(ioatdca->dca_base + IOAT3_CSI_CONTROL_OFFSET); + if ((csi_fsb_control & IOAT3_CSI_CONTROL_PREFETCH) == 0) { + csi_fsb_control |= IOAT3_CSI_CONTROL_PREFETCH; + writew(csi_fsb_control, + ioatdca->dca_base + IOAT3_CSI_CONTROL_OFFSET); + } + pcie_control = readw(ioatdca->dca_base + IOAT3_PCI_CONTROL_OFFSET); + if ((pcie_control & IOAT3_PCI_CONTROL_MEMWR) == 0) { + pcie_control |= IOAT3_PCI_CONTROL_MEMWR; + writew(pcie_control, + ioatdca->dca_base + IOAT3_PCI_CONTROL_OFFSET); + } + + + /* TODO version, compatibility and configuration checks */ + + /* copy out the APIC to DCA tag map */ + tag_map.low = + readl(ioatdca->dca_base + IOAT3_APICID_TAG_MAP_OFFSET_LOW); + tag_map.high = + readl(ioatdca->dca_base + IOAT3_APICID_TAG_MAP_OFFSET_HIGH); + for (i = 0; i < 8; i++) { + bit = tag_map.full >> (8 * i); + ioatdca->tag_map[i] = bit & DCA_TAG_MAP_MASK; + } + + err = register_dca_provider(dca, &pdev->dev); + if (err) { + free_dca_provider(dca); + return NULL; + } + + return dca; +} diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index ece5a0e3a335..a52156e56886 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -53,6 +53,12 @@ MODULE_PARM_DESC(ioat_pending_level, static void ioat_dma_chan_reset_part2(struct work_struct *work); static void ioat_dma_chan_watchdog(struct work_struct *work); +/* + * workaround for IOAT ver.3.0 null descriptor issue + * (channel returns error when size is 0) + */ +#define NULL_DESC_BUFFER_SIZE 1 + /* internal functions */ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan); static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan); @@ -129,6 +135,38 @@ static int ioat_dma_enumerate_channels(struct ioatdma_device *device) int i; struct ioat_dma_chan *ioat_chan; + /* + * IOAT ver.3 workarounds + */ + if (device->version == IOAT_VER_3_0) { + u32 chan_err_mask; + u16 dev_id; + u32 dmauncerrsts; + + /* + * Write CHANERRMSK_INT with 3E07h to mask out the errors + * that can cause stability issues for IOAT ver.3 + */ + chan_err_mask = 0x3E07; + pci_write_config_dword(device->pdev, + IOAT_PCI_CHANERRMASK_INT_OFFSET, + chan_err_mask); + + /* + * Clear DMAUNCERRSTS Cfg-Reg Parity Error status bit + * (workaround for spurious config parity error after restart) + */ + pci_read_config_word(device->pdev, + IOAT_PCI_DEVICE_ID_OFFSET, + &dev_id); + if (dev_id == PCI_DEVICE_ID_INTEL_IOAT_TBG0) { + dmauncerrsts = 0x10; + pci_write_config_dword(device->pdev, + IOAT_PCI_DMAUNCERRSTS_OFFSET, + dmauncerrsts); + } + } + device->common.chancnt = readb(device->reg_base + IOAT_CHANCNT_OFFSET); xfercap_scale = readb(device->reg_base + IOAT_XFERCAP_OFFSET); xfercap = (xfercap_scale == 0 ? -1 : (1UL << xfercap_scale)); @@ -473,6 +511,13 @@ static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) prev = new; } while (len && (new = ioat1_dma_get_next_descriptor(ioat_chan))); + if (!new) { + dev_err(&ioat_chan->device->pdev->dev, + "tx submit failed\n"); + spin_unlock_bh(&ioat_chan->desc_lock); + return -ENOMEM; + } + hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; if (new->async_tx.callback) { hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_INT_GN; @@ -558,7 +603,14 @@ static dma_cookie_t ioat2_tx_submit(struct dma_async_tx_descriptor *tx) desc_count++; } while (len && (new = ioat2_dma_get_next_descriptor(ioat_chan))); - hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; + if (!new) { + dev_err(&ioat_chan->device->pdev->dev, + "tx submit failed\n"); + spin_unlock_bh(&ioat_chan->desc_lock); + return -ENOMEM; + } + + hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_CP_STS; if (new->async_tx.callback) { hw->ctl |= IOAT_DMA_DESCRIPTOR_CTL_INT_GN; if (first != new) { @@ -629,6 +681,7 @@ static struct ioat_desc_sw *ioat_dma_alloc_descriptor( desc_sw->async_tx.tx_submit = ioat1_tx_submit; break; case IOAT_VER_2_0: + case IOAT_VER_3_0: desc_sw->async_tx.tx_submit = ioat2_tx_submit; break; } @@ -779,6 +832,7 @@ static void ioat_dma_free_chan_resources(struct dma_chan *chan) } break; case IOAT_VER_2_0: + case IOAT_VER_3_0: list_for_each_entry_safe(desc, _desc, ioat_chan->free_desc.next, node) { list_del(&desc->node); @@ -868,7 +922,8 @@ ioat2_dma_get_next_descriptor(struct ioat_dma_chan *ioat_chan) /* set up the noop descriptor */ noop_desc = to_ioat_desc(ioat_chan->used_desc.next); - noop_desc->hw->size = 0; + /* set size to non-zero value (channel returns error when size is 0) */ + noop_desc->hw->size = NULL_DESC_BUFFER_SIZE; noop_desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL; noop_desc->hw->src_addr = 0; noop_desc->hw->dst_addr = 0; @@ -918,6 +973,7 @@ static struct ioat_desc_sw *ioat_dma_get_next_descriptor( return ioat1_dma_get_next_descriptor(ioat_chan); break; case IOAT_VER_2_0: + case IOAT_VER_3_0: return ioat2_dma_get_next_descriptor(ioat_chan); break; } @@ -1061,10 +1117,12 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) * perhaps we're stuck so hard that the watchdog can't go off? * try to catch it after 2 seconds */ - if (time_after(jiffies, - ioat_chan->last_completion_time + HZ*WATCHDOG_DELAY)) { - ioat_dma_chan_watchdog(&(ioat_chan->device->work.work)); - ioat_chan->last_completion_time = jiffies; + if (ioat_chan->device->version != IOAT_VER_3_0) { + if (time_after(jiffies, + ioat_chan->last_completion_time + HZ*WATCHDOG_DELAY)) { + ioat_dma_chan_watchdog(&(ioat_chan->device->work.work)); + ioat_chan->last_completion_time = jiffies; + } } return; } @@ -1120,6 +1178,7 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan) } break; case IOAT_VER_2_0: + case IOAT_VER_3_0: /* has some other thread has already cleaned up? */ if (ioat_chan->used_desc.prev == NULL) break; @@ -1223,10 +1282,19 @@ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan) spin_lock_bh(&ioat_chan->desc_lock); desc = ioat_dma_get_next_descriptor(ioat_chan); + + if (!desc) { + dev_err(&ioat_chan->device->pdev->dev, + "Unable to start null desc - get next desc failed\n"); + spin_unlock_bh(&ioat_chan->desc_lock); + return; + } + desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL | IOAT_DMA_DESCRIPTOR_CTL_INT_GN | IOAT_DMA_DESCRIPTOR_CTL_CP_STS; - desc->hw->size = 0; + /* set size to non-zero value (channel returns error when size is 0) */ + desc->hw->size = NULL_DESC_BUFFER_SIZE; desc->hw->src_addr = 0; desc->hw->dst_addr = 0; async_tx_ack(&desc->async_tx); @@ -1244,6 +1312,7 @@ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan) + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); break; case IOAT_VER_2_0: + case IOAT_VER_3_0: writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_LOW); writel(((u64) desc->async_tx.phys) >> 32, @@ -1562,6 +1631,7 @@ struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, ioat1_dma_memcpy_issue_pending; break; case IOAT_VER_2_0: + case IOAT_VER_3_0: device->common.device_prep_dma_memcpy = ioat2_dma_prep_memcpy; device->common.device_issue_pending = ioat2_dma_memcpy_issue_pending; @@ -1585,9 +1655,11 @@ struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, dma_async_device_register(&device->common); - INIT_DELAYED_WORK(&device->work, ioat_dma_chan_watchdog); - schedule_delayed_work(&device->work, - WATCHDOG_DELAY); + if (device->version != IOAT_VER_3_0) { + INIT_DELAYED_WORK(&device->work, ioat_dma_chan_watchdog); + schedule_delayed_work(&device->work, + WATCHDOG_DELAY); + } return device; @@ -1621,7 +1693,9 @@ void ioat_dma_remove(struct ioatdma_device *device) pci_release_regions(device->pdev); pci_disable_device(device->pdev); - cancel_delayed_work(&device->work); + if (device->version != IOAT_VER_3_0) { + cancel_delayed_work(&device->work); + } list_for_each_entry_safe(chan, _chan, &device->common.channels, device_node) { diff --git a/drivers/dma/ioatdma.h b/drivers/dma/ioatdma.h index 685adb62aa5a..a3306d0e1372 100644 --- a/drivers/dma/ioatdma.h +++ b/drivers/dma/ioatdma.h @@ -29,7 +29,7 @@ #include #include -#define IOAT_DMA_VERSION "2.18" +#define IOAT_DMA_VERSION "3.30" enum ioat_interrupt { none = 0, @@ -135,6 +135,7 @@ static inline void ioat_set_tcp_copy_break(struct ioatdma_device *dev) #ifdef CONFIG_NET_DMA switch (dev->version) { case IOAT_VER_1_2: + case IOAT_VER_3_0: sysctl_tcp_dma_copybreak = 4096; break; case IOAT_VER_2_0: @@ -150,11 +151,13 @@ struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, void ioat_dma_remove(struct ioatdma_device *device); struct dca_provider *ioat_dca_init(struct pci_dev *pdev, void __iomem *iobase); struct dca_provider *ioat2_dca_init(struct pci_dev *pdev, void __iomem *iobase); +struct dca_provider *ioat3_dca_init(struct pci_dev *pdev, void __iomem *iobase); #else #define ioat_dma_probe(pdev, iobase) NULL #define ioat_dma_remove(device) do { } while (0) #define ioat_dca_init(pdev, iobase) NULL #define ioat2_dca_init(pdev, iobase) NULL +#define ioat3_dca_init(pdev, iobase) NULL #endif #endif /* IOATDMA_H */ diff --git a/drivers/dma/ioatdma_hw.h b/drivers/dma/ioatdma_hw.h index dd470fa91d86..f1ae2c776f74 100644 --- a/drivers/dma/ioatdma_hw.h +++ b/drivers/dma/ioatdma_hw.h @@ -35,6 +35,7 @@ #define IOAT_PCI_SID 0x8086 #define IOAT_VER_1_2 0x12 /* Version 1.2 */ #define IOAT_VER_2_0 0x20 /* Version 2.0 */ +#define IOAT_VER_3_0 0x30 /* Version 3.0 */ struct ioat_dma_descriptor { uint32_t size; diff --git a/drivers/dma/ioatdma_registers.h b/drivers/dma/ioatdma_registers.h index 9832d7ebd931..827cb503cac6 100644 --- a/drivers/dma/ioatdma_registers.h +++ b/drivers/dma/ioatdma_registers.h @@ -25,6 +25,10 @@ #define IOAT_PCI_DMACTRL_DMA_EN 0x00000001 #define IOAT_PCI_DMACTRL_MSI_EN 0x00000002 +#define IOAT_PCI_DEVICE_ID_OFFSET 0x02 +#define IOAT_PCI_DMAUNCERRSTS_OFFSET 0x148 +#define IOAT_PCI_CHANERRMASK_INT_OFFSET 0x184 + /* MMIO Device Registers */ #define IOAT_CHANCNT_OFFSET 0x00 /* 8-bit */ @@ -149,7 +153,23 @@ #define IOAT_DCA_GREQID_VALID 0x20000000 #define IOAT_DCA_GREQID_LASTID 0x80000000 +#define IOAT3_CSI_CAPABILITY_OFFSET 0x08 +#define IOAT3_CSI_CAPABILITY_PREFETCH 0x1 + +#define IOAT3_PCI_CAPABILITY_OFFSET 0x0A +#define IOAT3_PCI_CAPABILITY_MEMWR 0x1 + +#define IOAT3_CSI_CONTROL_OFFSET 0x0C +#define IOAT3_CSI_CONTROL_PREFETCH 0x1 + +#define IOAT3_PCI_CONTROL_OFFSET 0x0E +#define IOAT3_PCI_CONTROL_MEMWR 0x1 + +#define IOAT3_APICID_TAG_MAP_OFFSET 0x10 +#define IOAT3_APICID_TAG_MAP_OFFSET_LOW 0x10 +#define IOAT3_APICID_TAG_MAP_OFFSET_HIGH 0x14 +#define IOAT3_DCA_GREQID_OFFSET 0x02 #define IOAT1_CHAINADDR_OFFSET 0x0C /* 64-bit Descriptor Chain Address Register */ #define IOAT2_CHAINADDR_OFFSET 0x10 /* 64-bit Descriptor Chain Address Register */ diff --git a/include/linux/dca.h b/include/linux/dca.h index af61cd1f37e9..b00a753eda53 100644 --- a/include/linux/dca.h +++ b/include/linux/dca.h @@ -10,6 +10,7 @@ void dca_unregister_notify(struct notifier_block *nb); #define DCA_PROVIDER_REMOVE 0x0002 struct dca_provider { + struct list_head node; struct dca_ops *ops; struct device *cd; int id; @@ -18,7 +19,9 @@ struct dca_provider { struct dca_ops { int (*add_requester) (struct dca_provider *, struct device *); int (*remove_requester) (struct dca_provider *, struct device *); - u8 (*get_tag) (struct dca_provider *, int cpu); + u8 (*get_tag) (struct dca_provider *, struct device *, + int cpu); + int (*dev_managed) (struct dca_provider *, struct device *); }; struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size); @@ -32,9 +35,11 @@ static inline void *dca_priv(struct dca_provider *dca) } /* Requester API */ +#define DCA_GET_TAG_TWO_ARGS int dca_add_requester(struct device *dev); int dca_remove_requester(struct device *dev); u8 dca_get_tag(int cpu); +u8 dca3_get_tag(struct device *dev, int cpu); /* internal stuff */ int __init dca_sysfs_init(void); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 9b940e644179..06a5b7ae79be 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2363,6 +2363,14 @@ #define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 #define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 #define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a +#define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b +#define PCI_DEVICE_ID_INTEL_IOAT_TBG7 0x342c +#define PCI_DEVICE_ID_INTEL_IOAT_TBG0 0x3430 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG3 0x3433 #define PCI_DEVICE_ID_INTEL_82830_HB 0x3575 #define PCI_DEVICE_ID_INTEL_82830_CGC 0x3577 #define PCI_DEVICE_ID_INTEL_82855GM_HB 0x3580 -- cgit From e14fa82439d33cef67eaafc1a48960bbfa610c8e Mon Sep 17 00:00:00 2001 From: Riku Voipio Date: Sat, 31 May 2008 14:43:41 +0100 Subject: leds: Add pca9532 led driver NXP pca9532 is a LED dimmer/controller attached to i2c bus. It allows attaching upto 16 leds which can either be on, off or dimmed and/or blinked with the two PWM modulators available. This driver is a "new-style" i2c driver that adheres to the driver model and implements the led framework api. Since the leds connected to the driver are platform specific, it is only useful when platform data is passed to the driver to define what leds are connected to which pins. Signed-off-by: Riku Voipio Signed-off-by: Andrew Morton Signed-off-by: Richard Purdie --- drivers/leds/Kconfig | 8 + drivers/leds/Makefile | 1 + drivers/leds/leds-pca9532.c | 337 +++++++++++++++++++++++++++++++++++++++++++ include/linux/leds-pca9532.h | 45 ++++++ 4 files changed, 391 insertions(+) create mode 100644 drivers/leds/leds-pca9532.c create mode 100644 include/linux/leds-pca9532.h (limited to 'include/linux') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 86a369bc57d6..1c35dfaef721 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -103,6 +103,14 @@ config LEDS_HP6XX This option enables led support for the handheld HP Jornada 620/660/680/690. +config LEDS_PCA9532 + tristate "LED driver for PCA9532 dimmer" + depends on LEDS_CLASS && I2C && INPUT && EXPERIMENTAL + help + This option enables support for NXP pca9532 + led controller. It is generally only usefull + as a platform driver + config LEDS_GPIO tristate "LED Support for GPIO connected LEDs" depends on LEDS_CLASS && GENERIC_GPIO diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 973d626f5f4a..7156f9970fa9 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o obj-$(CONFIG_LEDS_H1940) += leds-h1940.o obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o +obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c new file mode 100644 index 000000000000..4064d4f6b33b --- /dev/null +++ b/drivers/leds/leds-pca9532.c @@ -0,0 +1,337 @@ +/* + * pca9532.c - 16-bit Led dimmer + * + * Copyright (C) 2008 Riku Voipio + * + * 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. + * + * Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf + * + */ + +#include +#include +#include +#include +#include +#include + +static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; +I2C_CLIENT_INSMOD_1(pca9532); + +#define PCA9532_REG_PSC(i) (0x2+(i)*2) +#define PCA9532_REG_PWM(i) (0x3+(i)*2) +#define PCA9532_REG_LS0 0x6 +#define LED_REG(led) ((led>>2)+PCA9532_REG_LS0) +#define LED_NUM(led) (led & 0x3) + +#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev) + +struct pca9532_data { + struct i2c_client *client; + struct pca9532_led leds[16]; + struct mutex update_lock; + struct input_dev *idev; + u8 pwm[2]; + u8 psc[2]; +}; + +static int pca9532_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int pca9532_remove(struct i2c_client *client); + +static const struct i2c_device_id pca9532_id[] = { + { "pca9532", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, pca9532_id); + +static struct i2c_driver pca9532_driver = { + .driver = { + .name = "pca9532", + }, + .probe = pca9532_probe, + .remove = pca9532_remove, + .id_table = pca9532_id, +}; + +/* We have two pwm/blinkers, but 16 possible leds to drive. Additionaly, + * the clever Thecus people are using one pwm to drive the beeper. So, + * as a compromise we average one pwm to the values requested by all + * leds that are not ON/OFF. + * */ +static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, + enum led_brightness value) +{ + int a = 0, b = 0, i = 0; + struct pca9532_data *data = i2c_get_clientdata(client); + for (i = 0; i < 16; i++) { + if (data->leds[i].type == PCA9532_TYPE_LED && + data->leds[i].state == PCA9532_PWM0+pwm) { + a++; + b += data->leds[i].ldev.brightness; + } + } + if (a == 0) { + dev_err(&client->dev, + "fear of division by zero %d/%d, wanted %d\n", + b, a, value); + return -EINVAL; + } + b = b/a; + if (b > 0xFF) + return -EINVAL; + mutex_lock(&data->update_lock); + data->pwm[pwm] = b; + i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), + data->pwm[pwm]); + data->psc[pwm] = blink; + i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), + data->psc[pwm]); + mutex_unlock(&data->update_lock); + return 0; +} + +/* Set LED routing */ +static void pca9532_setled(struct pca9532_led *led) +{ + struct i2c_client *client = led->client; + struct pca9532_data *data = i2c_get_clientdata(client); + char reg; + + mutex_lock(&data->update_lock); + reg = i2c_smbus_read_byte_data(client, LED_REG(led->id)); + /* zero led bits */ + reg = reg & ~(0x3<id)*2); + /* set the new value */ + reg = reg | (led->state << LED_NUM(led->id)*2); + i2c_smbus_write_byte_data(client, LED_REG(led->id), reg); + mutex_unlock(&data->update_lock); +} + +static void pca9532_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + int err = 0; + struct pca9532_led *led = ldev_to_led(led_cdev); + + if (value == LED_OFF) + led->state = PCA9532_OFF; + else if (value == LED_FULL) + led->state = PCA9532_ON; + else { + led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ + err = pca9532_setpwm(led->client, 0, 0, value); + if (err) + return; /* XXX: led api doesn't allow error code? */ + } + pca9532_setled(led); +} + +static int pca9532_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, unsigned long *delay_off) +{ + struct pca9532_led *led = ldev_to_led(led_cdev); + struct i2c_client *client = led->client; + int psc; + + if (*delay_on == 0 && *delay_off == 0) { + /* led subsystem ask us for a blink rate */ + *delay_on = 1000; + *delay_off = 1000; + } + if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6) + return -EINVAL; + + /* Thecus specific: only use PSC/PWM 0 */ + psc = (*delay_on * 152-1)/1000; + return pca9532_setpwm(client, 0, psc, led_cdev->brightness); +} + +int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, + int value) +{ + struct pca9532_data *data = input_get_drvdata(dev); + + if (type != EV_SND && (code != SND_BELL || code != SND_TONE)) + return -1; + + /* XXX: allow different kind of beeps with psc/pwm modifications */ + if (value > 1 && value < 32767) + data->pwm[1] = 127; + else + data->pwm[1] = 0; + + dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]); + mutex_lock(&data->update_lock); + i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), + data->pwm[1]); + mutex_unlock(&data->update_lock); + + return 0; +} + +static int pca9532_configure(struct i2c_client *client, + struct pca9532_data *data, struct pca9532_platform_data *pdata) +{ + int i, err = 0; + + for (i = 0; i < 2; i++) { + data->pwm[i] = pdata->pwm[i]; + data->psc[i] = pdata->psc[i]; + i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(i), + data->pwm[i]); + i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(i), + data->psc[i]); + } + + for (i = 0; i < 16; i++) { + struct pca9532_led *led = &data->leds[i]; + struct pca9532_led *pled = &pdata->leds[i]; + led->client = client; + led->id = i; + led->type = pled->type; + switch (led->type) { + case PCA9532_TYPE_NONE: + break; + case PCA9532_TYPE_LED: + led->state = pled->state; + led->name = pled->name; + led->ldev.name = led->name; + led->ldev.brightness = LED_OFF; + led->ldev.brightness_set = pca9532_set_brightness; + led->ldev.blink_set = pca9532_set_blink; + if (led_classdev_register(&client->dev, + &led->ldev) < 0) { + dev_err(&client->dev, + "couldn't register LED %s\n", + led->name); + goto exit; + } + pca9532_setled(led); + break; + case PCA9532_TYPE_N2100_BEEP: + BUG_ON(data->idev); + led->state = PCA9532_PWM1; + pca9532_setled(led); + data->idev = input_allocate_device(); + if (data->idev == NULL) { + err = -ENOMEM; + goto exit; + } + data->idev->name = pled->name; + data->idev->phys = "i2c/pca9532"; + data->idev->id.bustype = BUS_HOST; + data->idev->id.vendor = 0x001f; + data->idev->id.product = 0x0001; + data->idev->id.version = 0x0100; + data->idev->evbit[0] = BIT_MASK(EV_SND); + data->idev->sndbit[0] = BIT_MASK(SND_BELL) | + BIT_MASK(SND_TONE); + data->idev->event = pca9532_event; + input_set_drvdata(data->idev, data); + err = input_register_device(data->idev); + if (err) { + input_free_device(data->idev); + data->idev = NULL; + goto exit; + } + break; + } + } + return 0; + +exit: + if (i > 0) + for (i = i - 1; i >= 0; i--) + switch (data->leds[i].type) { + case PCA9532_TYPE_NONE: + break; + case PCA9532_TYPE_LED: + led_classdev_unregister(&data->leds[i].ldev); + break; + case PCA9532_TYPE_N2100_BEEP: + if (data->idev != NULL) { + input_unregister_device(data->idev); + input_free_device(data->idev); + data->idev = NULL; + } + break; + } + + return err; + +} + +static int pca9532_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca9532_data *data = i2c_get_clientdata(client); + struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + dev_info(&client->dev, "setting platform data\n"); + i2c_set_clientdata(client, data); + data->client = client; + mutex_init(&data->update_lock); + + if (pca9532_pdata == NULL) + return -EIO; + + pca9532_configure(client, data, pca9532_pdata); + return 0; + +} + +static int pca9532_remove(struct i2c_client *client) +{ + struct pca9532_data *data = i2c_get_clientdata(client); + int i; + for (i = 0; i < 16; i++) + switch (data->leds[i].type) { + case PCA9532_TYPE_NONE: + break; + case PCA9532_TYPE_LED: + led_classdev_unregister(&data->leds[i].ldev); + break; + case PCA9532_TYPE_N2100_BEEP: + if (data->idev != NULL) { + input_unregister_device(data->idev); + input_free_device(data->idev); + data->idev = NULL; + } + break; + } + + kfree(data); + i2c_set_clientdata(client, NULL); + return 0; +} + +static int __init pca9532_init(void) +{ + return i2c_add_driver(&pca9532_driver); +} + +static void __exit pca9532_exit(void) +{ + i2c_del_driver(&pca9532_driver); +} + +MODULE_AUTHOR("Riku Voipio "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PCA 9532 LED dimmer"); + +module_init(pca9532_init); +module_exit(pca9532_exit); + diff --git a/include/linux/leds-pca9532.h b/include/linux/leds-pca9532.h new file mode 100644 index 000000000000..81b4207deb95 --- /dev/null +++ b/include/linux/leds-pca9532.h @@ -0,0 +1,45 @@ +/* + * pca9532.h - platform data structure for pca9532 led controller + * + * Copyright (C) 2008 Riku Voipio + * + * 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. + * + * Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf + * + */ + +#ifndef __LINUX_PCA9532_H +#define __LINUX_PCA9532_H + +#include + +enum pca9532_state { + PCA9532_OFF = 0x0, + PCA9532_ON = 0x1, + PCA9532_PWM0 = 0x2, + PCA9532_PWM1 = 0x3 +}; + +enum pca9532_type { PCA9532_TYPE_NONE, PCA9532_TYPE_LED, + PCA9532_TYPE_N2100_BEEP }; + +struct pca9532_led { + u8 id; + struct i2c_client *client; + char *name; + struct led_classdev ldev; + enum pca9532_type type; + enum pca9532_state state; +}; + +struct pca9532_platform_data { + struct pca9532_led leds[16]; + u8 pwm[2]; + u8 psc[2]; +}; + +#endif /* __LINUX_PCA9532_H */ + -- cgit From 781a54e7664cc0089287a90d27086e9656ac68a1 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 31 May 2008 15:23:19 +0100 Subject: leds: mark led_classdev.default_trigger as const LED classdev core doesn't modify memory pointed by the default_trigger, so mark it as const and we'll able to pass const char *s without getting compiler warnings. Signed-off-by: Anton Vorontsov Signed-off-by: Richard Purdie --- include/linux/leds.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/leds.h b/include/linux/leds.h index 519df72e939d..e7a5e89932fe 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -48,7 +48,7 @@ struct led_classdev { struct device *dev; struct list_head node; /* LED Device list */ - char *default_trigger; /* Trigger to use */ + const char *default_trigger; /* Trigger to use */ #ifdef CONFIG_LEDS_TRIGGERS /* Protects the trigger data below */ -- cgit From f46e9203d9a100bae216cc06e17f2e77351aa8d8 Mon Sep 17 00:00:00 2001 From: Nate Case Date: Wed, 16 Jul 2008 22:49:55 +0100 Subject: leds: Add support for Philips PCA955x I2C LED drivers This driver supports the PCA9550, PCA9551, PCA9552, and PCA9553 LED driver chips. Signed-off-by: Nate Case Signed-off-by: Andrew Morton Signed-off-by: Richard Purdie --- drivers/leds/Kconfig | 8 + drivers/leds/Makefile | 1 + drivers/leds/leds-pca955x.c | 384 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/leds.h | 14 ++ 4 files changed, 407 insertions(+) create mode 100644 drivers/leds/leds-pca955x.c (limited to 'include/linux') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1c35dfaef721..9556262dda5a 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -155,6 +155,14 @@ config LEDS_CLEVO_MAIL To compile this driver as a module, choose M here: the module will be called leds-clevo-mail. +config LEDS_PCA955X + tristate "LED Support for PCA955x I2C chips" + depends on LEDS_CLASS && I2C + help + This option enables support for LEDs connected to PCA955x + LED driver chips accessed via the I2C bus. Supported + devices include PCA9550, PCA9551, PCA9552, and PCA9553. + comment "LED Triggers" config LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 7156f9970fa9..ff7982b44565 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o +obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c new file mode 100644 index 000000000000..146c06972863 --- /dev/null +++ b/drivers/leds/leds-pca955x.c @@ -0,0 +1,384 @@ +/* + * Copyright 2007-2008 Extreme Engineering Solutions, Inc. + * + * Author: Nate Case + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * LED driver for various PCA955x I2C LED drivers + * + * Supported devices: + * + * Device Description 7-bit slave address + * ------ ----------- ------------------- + * PCA9550 2-bit driver 0x60 .. 0x61 + * PCA9551 8-bit driver 0x60 .. 0x67 + * PCA9552 16-bit driver 0x60 .. 0x67 + * PCA9553/01 4-bit driver 0x62 + * PCA9553/02 4-bit driver 0x63 + * + * Philips PCA955x LED driver chips follow a register map as shown below: + * + * Control Register Description + * ---------------- ----------- + * 0x0 Input register 0 + * .. + * NUM_INPUT_REGS - 1 Last Input register X + * + * NUM_INPUT_REGS Frequency prescaler 0 + * NUM_INPUT_REGS + 1 PWM register 0 + * NUM_INPUT_REGS + 2 Frequency prescaler 1 + * NUM_INPUT_REGS + 3 PWM register 1 + * + * NUM_INPUT_REGS + 4 LED selector 0 + * NUM_INPUT_REGS + 4 + * + NUM_LED_REGS - 1 Last LED selector + * + * where NUM_INPUT_REGS and NUM_LED_REGS vary depending on how many + * bits the chip supports. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* LED select registers determine the source that drives LED outputs */ +#define PCA955X_LS_LED_ON 0x0 /* Output LOW */ +#define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */ +#define PCA955X_LS_BLINK0 0x2 /* Blink at PWM0 rate */ +#define PCA955X_LS_BLINK1 0x3 /* Blink at PWM1 rate */ + +enum pca955x_type { + pca9550, + pca9551, + pca9552, + pca9553, +}; + +struct pca955x_chipdef { + int bits; + u8 slv_addr; /* 7-bit slave address mask */ + int slv_addr_shift; /* Number of bits to ignore */ +}; + +static struct pca955x_chipdef pca955x_chipdefs[] = { + [pca9550] = { + .bits = 2, + .slv_addr = /* 110000x */ 0x60, + .slv_addr_shift = 1, + }, + [pca9551] = { + .bits = 8, + .slv_addr = /* 1100xxx */ 0x60, + .slv_addr_shift = 3, + }, + [pca9552] = { + .bits = 16, + .slv_addr = /* 1100xxx */ 0x60, + .slv_addr_shift = 3, + }, + [pca9553] = { + .bits = 4, + .slv_addr = /* 110001x */ 0x62, + .slv_addr_shift = 1, + }, +}; + +static const struct i2c_device_id pca955x_id[] = { + { "pca9550", pca9550 }, + { "pca9551", pca9551 }, + { "pca9552", pca9552 }, + { "pca9553", pca9553 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca955x_id); + +struct pca955x_led { + struct pca955x_chipdef *chipdef; + struct i2c_client *client; + struct work_struct work; + spinlock_t lock; + enum led_brightness brightness; + struct led_classdev led_cdev; + int led_num; /* 0 .. 15 potentially */ + char name[32]; +}; + +/* 8 bits per input register */ +static inline int pca95xx_num_input_regs(int bits) +{ + return (bits + 7) / 8; +} + +/* 4 bits per LED selector register */ +static inline int pca95xx_num_led_regs(int bits) +{ + return (bits + 3) / 4; +} + +/* + * Return an LED selector register value based on an existing one, with + * the appropriate 2-bit state value set for the given LED number (0-3). + */ +static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) +{ + return (oldval & (~(0x3 << (led_num << 1)))) | + ((state & 0x3) << (led_num << 1)); +} + +/* + * Write to frequency prescaler register, used to program the + * period of the PWM output. period = (PSCx + 1) / 38 + */ +static void pca955x_write_psc(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, + val); +} + +/* + * Write to PWM register, which determines the duty cycle of the + * output. LED is OFF when the count is less than the value of this + * register, and ON when it is greater. If PWMx == 0, LED is always OFF. + * + * Duty cycle is (256 - PWMx) / 256 + */ +static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, + val); +} + +/* + * Write to LED selector register, which determines the source that + * drives the LED output. + */ +static void pca955x_write_ls(struct i2c_client *client, int n, u8 val) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, + val); +} + +/* + * Read the LED selector register, which determines the source that + * drives the LED output. + */ +static u8 pca955x_read_ls(struct i2c_client *client, int n) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + + return (u8) i2c_smbus_read_byte_data(client, + pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); +} + +static void pca955x_led_work(struct work_struct *work) +{ + struct pca955x_led *pca955x; + u8 ls; + int chip_ls; /* which LSx to use (0-3 potentially) */ + int ls_led; /* which set of bits within LSx to use (0-3) */ + + pca955x = container_of(work, struct pca955x_led, work); + chip_ls = pca955x->led_num / 4; + ls_led = pca955x->led_num % 4; + + ls = pca955x_read_ls(pca955x->client, chip_ls); + + switch (pca955x->brightness) { + case LED_FULL: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON); + break; + case LED_OFF: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF); + break; + case LED_HALF: + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0); + break; + default: + /* + * Use PWM1 for all other values. This has the unwanted + * side effect of making all LEDs on the chip share the + * same brightness level if set to a value other than + * OFF, HALF, or FULL. But, this is probably better than + * just turning off for all other values. + */ + pca955x_write_pwm(pca955x->client, 1, 255-pca955x->brightness); + ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1); + break; + } + + pca955x_write_ls(pca955x->client, chip_ls, ls); +} + +void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + struct pca955x_led *pca955x; + + pca955x = container_of(led_cdev, struct pca955x_led, led_cdev); + + spin_lock(&pca955x->lock); + pca955x->brightness = value; + + /* + * Must use workqueue for the actual I/O since I2C operations + * can sleep. + */ + schedule_work(&pca955x->work); + + spin_unlock(&pca955x->lock); +} + +static int __devinit pca955x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca955x_led *pca955x; + int i; + int err = -ENODEV; + struct pca955x_chipdef *chip; + struct i2c_adapter *adapter; + struct led_platform_data *pdata; + + chip = &pca955x_chipdefs[id->driver_data]; + adapter = to_i2c_adapter(client->dev.parent); + pdata = client->dev.platform_data; + + /* Make sure the slave address / chip type combo given is possible */ + if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != + chip->slv_addr) { + dev_err(&client->dev, "invalid slave address %02x\n", + client->addr); + return -ENODEV; + } + + printk(KERN_INFO "leds-pca955x: Using %s %d-bit LED driver at " + "slave address 0x%02x\n", + id->name, chip->bits, client->addr); + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -EIO; + + if (pdata) { + if (pdata->num_leds != chip->bits) { + dev_err(&client->dev, "board info claims %d LEDs" + " on a %d-bit chip\n", + pdata->num_leds, chip->bits); + return -ENODEV; + } + } + + for (i = 0; i < chip->bits; i++) { + pca955x = kzalloc(sizeof(struct pca955x_led), GFP_KERNEL); + if (!pca955x) { + err = -ENOMEM; + goto exit; + } + + pca955x->chipdef = chip; + pca955x->client = client; + pca955x->led_num = i; + /* Platform data can specify LED names and default triggers */ + if (pdata) { + if (pdata->leds[i].name) + snprintf(pca955x->name, 32, "pca955x:%s", + pdata->leds[i].name); + if (pdata->leds[i].default_trigger) + pca955x->led_cdev.default_trigger = + pdata->leds[i].default_trigger; + } else { + snprintf(pca955x->name, 32, "pca955x:%d", i); + } + spin_lock_init(&pca955x->lock); + + pca955x->led_cdev.name = pca955x->name; + pca955x->led_cdev.brightness_set = + pca955x_led_set; + + /* + * Client data is a pointer to the _first_ pca955x_led + * struct + */ + if (i == 0) + i2c_set_clientdata(client, pca955x); + + INIT_WORK(&(pca955x->work), pca955x_led_work); + + led_classdev_register(&client->dev, &(pca955x->led_cdev)); + } + + /* Turn off LEDs */ + for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++) + pca955x_write_ls(client, i, 0x55); + + /* PWM0 is used for half brightness or 50% duty cycle */ + pca955x_write_pwm(client, 0, 255-LED_HALF); + + /* PWM1 is used for variable brightness, default to OFF */ + pca955x_write_pwm(client, 1, 0); + + /* Set to fast frequency so we do not see flashing */ + pca955x_write_psc(client, 0, 0); + pca955x_write_psc(client, 1, 0); + + return 0; +exit: + return err; +} + +static int __devexit pca955x_remove(struct i2c_client *client) +{ + struct pca955x_led *pca955x = i2c_get_clientdata(client); + int leds = pca955x->chipdef->bits; + int i; + + for (i = 0; i < leds; i++) { + led_classdev_unregister(&(pca955x->led_cdev)); + cancel_work_sync(&(pca955x->work)); + kfree(pca955x); + pca955x = pca955x + 1; + } + + return 0; +} + +static struct i2c_driver pca955x_driver = { + .driver = { + .name = "leds-pca955x", + .owner = THIS_MODULE, + }, + .probe = pca955x_probe, + .remove = __devexit_p(pca955x_remove), + .id_table = pca955x_id, +}; + +static int __init pca955x_leds_init(void) +{ + return i2c_add_driver(&pca955x_driver); +} + +static void __exit pca955x_leds_exit(void) +{ + i2c_del_driver(&pca955x_driver); +} + +module_init(pca955x_leds_init); +module_exit(pca955x_leds_exit); + +MODULE_AUTHOR("Nate Case "); +MODULE_DESCRIPTION("PCA955x LED driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/leds.h b/include/linux/leds.h index e7a5e89932fe..d41ccb56146a 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -118,6 +118,20 @@ extern void ledtrig_ide_activity(void); #define ledtrig_ide_activity() do {} while(0) #endif +/* + * Generic LED platform data for describing LED names and default triggers. + */ +struct led_info { + const char *name; + char *default_trigger; + int flags; +}; + +struct led_platform_data { + int num_leds; + struct led_info *leds; +}; + /* For the leds-gpio driver */ struct gpio_led { const char *name; -- cgit From 137d3edb48425f82a6a4226b664f90ed5e42eea5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 19 Jul 2008 23:03:35 +0900 Subject: sg: reimplement sg mapping iterator This is alternative implementation of sg content iterator introduced by commit 83e7d317... from Pierre Ossman in next-20080716. As there's already an sg iterator which iterates over sg entries themselves, name this sg_mapping_iterator. Slightly edited description from the original implementation follows. Iteration over a sg list is not that trivial when you take into account that memory pages might have to be mapped before being used. Unfortunately, that means that some parts of the kernel restrict themselves to directly accesible memory just to not have to deal with the mess. This patch adds a simple iterator system that allows any code to easily traverse an sg list and not have to deal with all the details. The user can decide to consume part of the iteration. Also, iteration can be stopped and resumed later if releasing the kmap between iteration steps is necessary. These features are useful to implement piecemeal sg copying for interrupt drive PIO for example. Signed-off-by: Tejun Heo Signed-off-by: Pierre Ossman --- include/linux/scatterlist.h | 38 ++++++++++ lib/scatterlist.c | 176 ++++++++++++++++++++++++++++++++------------ 2 files changed, 168 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 71fc81360048..e5996984ddd0 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -224,4 +224,42 @@ size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents, */ #define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist)) + +/* + * Mapping sg iterator + * + * Iterates over sg entries mapping page-by-page. On each successful + * iteration, @miter->page points to the mapped page and + * @miter->length bytes of data can be accessed at @miter->addr. As + * long as an interation is enclosed between start and stop, the user + * is free to choose control structure and when to stop. + * + * @miter->consumed is set to @miter->length on each iteration. It + * can be adjusted if the user can't consume all the bytes in one go. + * Also, a stopped iteration can be resumed by calling next on it. + * This is useful when iteration needs to release all resources and + * continue later (e.g. at the next interrupt). + */ + +#define SG_MITER_ATOMIC (1 << 0) /* use kmap_atomic */ + +struct sg_mapping_iter { + /* the following three fields can be accessed directly */ + struct page *page; /* currently mapped page */ + void *addr; /* pointer to the mapped area */ + size_t length; /* length of the mapped area */ + size_t consumed; /* number of consumed bytes */ + + /* these are internal states, keep away */ + struct scatterlist *__sg; /* current entry */ + unsigned int __nents; /* nr of remaining entries */ + unsigned int __offset; /* offset within sg */ + unsigned int __flags; +}; + +void sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl, + unsigned int nents, unsigned int flags); +bool sg_miter_next(struct sg_mapping_iter *miter); +void sg_miter_stop(struct sg_mapping_iter *miter); + #endif /* _LINUX_SCATTERLIST_H */ diff --git a/lib/scatterlist.c b/lib/scatterlist.c index b80c21100d78..876ba6d5b670 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -294,6 +294,117 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) } EXPORT_SYMBOL(sg_alloc_table); +/** + * sg_miter_start - start mapping iteration over a sg list + * @miter: sg mapping iter to be started + * @sgl: sg list to iterate over + * @nents: number of sg entries + * + * Description: + * Starts mapping iterator @miter. + * + * Context: + * Don't care. + */ +void sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl, + unsigned int nents, unsigned int flags) +{ + memset(miter, 0, sizeof(struct sg_mapping_iter)); + + miter->__sg = sgl; + miter->__nents = nents; + miter->__offset = 0; + miter->__flags = flags; +} +EXPORT_SYMBOL(sg_miter_start); + +/** + * sg_miter_next - proceed mapping iterator to the next mapping + * @miter: sg mapping iter to proceed + * + * Description: + * Proceeds @miter@ to the next mapping. @miter@ should have been + * started using sg_miter_start(). On successful return, + * @miter@->page, @miter@->addr and @miter@->length point to the + * current mapping. + * + * Context: + * IRQ disabled if SG_MITER_ATOMIC. IRQ must stay disabled till + * @miter@ is stopped. May sleep if !SG_MITER_ATOMIC. + * + * Returns: + * true if @miter contains the next mapping. false if end of sg + * list is reached. + */ +bool sg_miter_next(struct sg_mapping_iter *miter) +{ + unsigned int off, len; + + /* check for end and drop resources from the last iteration */ + if (!miter->__nents) + return false; + + sg_miter_stop(miter); + + /* get to the next sg if necessary. __offset is adjusted by stop */ + if (miter->__offset == miter->__sg->length && --miter->__nents) { + miter->__sg = sg_next(miter->__sg); + miter->__offset = 0; + } + + /* map the next page */ + off = miter->__sg->offset + miter->__offset; + len = miter->__sg->length - miter->__offset; + + miter->page = nth_page(sg_page(miter->__sg), off >> PAGE_SHIFT); + off &= ~PAGE_MASK; + miter->length = min_t(unsigned int, len, PAGE_SIZE - off); + miter->consumed = miter->length; + + if (miter->__flags & SG_MITER_ATOMIC) + miter->addr = kmap_atomic(miter->page, KM_BIO_SRC_IRQ) + off; + else + miter->addr = kmap(miter->page) + off; + + return true; +} +EXPORT_SYMBOL(sg_miter_next); + +/** + * sg_miter_stop - stop mapping iteration + * @miter: sg mapping iter to be stopped + * + * Description: + * Stops mapping iterator @miter. @miter should have been started + * started using sg_miter_start(). A stopped iteration can be + * resumed by calling sg_miter_next() on it. This is useful when + * resources (kmap) need to be released during iteration. + * + * Context: + * IRQ disabled if the SG_MITER_ATOMIC is set. Don't care otherwise. + */ +void sg_miter_stop(struct sg_mapping_iter *miter) +{ + WARN_ON(miter->consumed > miter->length); + + /* drop resources from the last iteration */ + if (miter->addr) { + miter->__offset += miter->consumed; + + if (miter->__flags & SG_MITER_ATOMIC) { + WARN_ON(!irqs_disabled()); + kunmap_atomic(miter->addr, KM_BIO_SRC_IRQ); + } else + kunmap(miter->addr); + + miter->page = NULL; + miter->addr = NULL; + miter->length = 0; + miter->consumed = 0; + } +} +EXPORT_SYMBOL(sg_miter_stop); + /** * sg_copy_buffer - Copy data between a linear buffer and an SG list * @sgl: The SG list @@ -309,56 +420,29 @@ EXPORT_SYMBOL(sg_alloc_table); static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, size_t buflen, int to_buffer) { - struct scatterlist *sg; - size_t buf_off = 0; - int i; - - WARN_ON(!irqs_disabled()); - - for_each_sg(sgl, sg, nents, i) { - struct page *page; - int n = 0; - unsigned int sg_off = sg->offset; - unsigned int sg_copy = sg->length; - - if (sg_copy > buflen) - sg_copy = buflen; - buflen -= sg_copy; - - while (sg_copy > 0) { - unsigned int page_copy; - void *p; - - page_copy = PAGE_SIZE - sg_off; - if (page_copy > sg_copy) - page_copy = sg_copy; - - page = nth_page(sg_page(sg), n); - p = kmap_atomic(page, KM_BIO_SRC_IRQ); - - if (to_buffer) - memcpy(buf + buf_off, p + sg_off, page_copy); - else { - memcpy(p + sg_off, buf + buf_off, page_copy); - flush_kernel_dcache_page(page); - } - - kunmap_atomic(p, KM_BIO_SRC_IRQ); - - buf_off += page_copy; - sg_off += page_copy; - if (sg_off == PAGE_SIZE) { - sg_off = 0; - n++; - } - sg_copy -= page_copy; + unsigned int offset = 0; + struct sg_mapping_iter miter; + + sg_miter_start(&miter, sgl, nents, SG_MITER_ATOMIC); + + while (sg_miter_next(&miter) && offset < buflen) { + unsigned int len; + + len = min(miter.length, buflen - offset); + + if (to_buffer) + memcpy(buf + offset, miter.addr, len); + else { + memcpy(miter.addr, buf + offset, len); + flush_kernel_dcache_page(miter.page); } - if (!buflen) - break; + offset += len; } - return buf_off; + sg_miter_stop(&miter); + + return offset; } /** -- cgit From f472f80034ae5d0be7d7196c7e65d0ca8890afb4 Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Thu, 19 Jun 2008 11:51:04 +0200 Subject: HID: add n-trig digitizer usage This adds a hid usage that is reported by the N-Trig digitizer in the Dell Latitude XT screen. Signed-off-by: Rafi Rubin Signed-off-by: Vojtech Pavlik Signed-off-by: Jiri Kosina --- include/linux/hid.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/hid.h b/include/linux/hid.h index fe56b86f2c67..ac4e678a04ed 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -512,7 +512,7 @@ struct hid_descriptor { /* Applications from HID Usage Tables 4/8/99 Version 1.1 */ /* We ignore a few input applications that are not widely used */ -#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001)) +#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002)) /* HID core API */ -- cgit From 95d04f0735b4fc837bff9aedcc3f3efb20ddc3d1 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 23 Jul 2008 08:12:26 -0700 Subject: IB/mlx4: Add support for memory management extensions and local DMA L_Key Add support for the following operations to mlx4 when device firmware supports them: - Send with invalidate and local invalidate send queue work requests; - Allocate/free fast register MRs; - Allocate/free fast register MR page lists; - Fast register MR send queue work requests; - Local DMA L_Key. Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/cq.c | 12 ++++++ drivers/infiniband/hw/mlx4/main.c | 11 ++++++ drivers/infiniband/hw/mlx4/mlx4_ib.h | 15 ++++++++ drivers/infiniband/hw/mlx4/mr.c | 70 +++++++++++++++++++++++++++++++++++ drivers/infiniband/hw/mlx4/qp.c | 72 +++++++++++++++++++++++++++++++++--- drivers/net/mlx4/fw.c | 10 ++--- drivers/net/mlx4/fw.h | 2 +- drivers/net/mlx4/main.c | 2 + drivers/net/mlx4/mr.c | 23 +++++++++--- include/linux/mlx4/device.h | 10 +++++ include/linux/mlx4/qp.h | 16 ++++++-- 11 files changed, 221 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index 299f20832ab6..0b191a4842ce 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -637,6 +637,7 @@ repoll: case MLX4_OPCODE_SEND_IMM: wc->wc_flags |= IB_WC_WITH_IMM; case MLX4_OPCODE_SEND: + case MLX4_OPCODE_SEND_INVAL: wc->opcode = IB_WC_SEND; break; case MLX4_OPCODE_RDMA_READ: @@ -657,6 +658,12 @@ repoll: case MLX4_OPCODE_LSO: wc->opcode = IB_WC_LSO; break; + case MLX4_OPCODE_FMR: + wc->opcode = IB_WC_FAST_REG_MR; + break; + case MLX4_OPCODE_LOCAL_INVAL: + wc->opcode = IB_WC_LOCAL_INV; + break; } } else { wc->byte_len = be32_to_cpu(cqe->byte_cnt); @@ -667,6 +674,11 @@ repoll: wc->wc_flags = IB_WC_WITH_IMM; wc->ex.imm_data = cqe->immed_rss_invalid; break; + case MLX4_RECV_OPCODE_SEND_INVAL: + wc->opcode = IB_WC_RECV; + wc->wc_flags = IB_WC_WITH_INVALIDATE; + wc->ex.invalidate_rkey = be32_to_cpu(cqe->immed_rss_invalid); + break; case MLX4_RECV_OPCODE_SEND: wc->opcode = IB_WC_RECV; wc->wc_flags = 0; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index bcf50648fa18..38d6907ab521 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -104,6 +104,12 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->device_cap_flags |= IB_DEVICE_UD_IP_CSUM; if (dev->dev->caps.max_gso_sz) props->device_cap_flags |= IB_DEVICE_UD_TSO; + if (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_RESERVED_LKEY) + props->device_cap_flags |= IB_DEVICE_LOCAL_DMA_LKEY; + if ((dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_LOCAL_INV) && + (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_REMOTE_INV) && + (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_FAST_REG_WR)) + props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS; props->vendor_id = be32_to_cpup((__be32 *) (out_mad->data + 36)) & 0xffffff; @@ -127,6 +133,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->max_srq = dev->dev->caps.num_srqs - dev->dev->caps.reserved_srqs; props->max_srq_wr = dev->dev->caps.max_srq_wqes - 1; props->max_srq_sge = dev->dev->caps.max_srq_sge; + props->max_fast_reg_page_list_len = PAGE_SIZE / sizeof (u64); props->local_ca_ack_delay = dev->dev->caps.local_ca_ack_delay; props->atomic_cap = dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_ATOMIC ? IB_ATOMIC_HCA : IB_ATOMIC_NONE; @@ -565,6 +572,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) strlcpy(ibdev->ib_dev.name, "mlx4_%d", IB_DEVICE_NAME_MAX); ibdev->ib_dev.owner = THIS_MODULE; ibdev->ib_dev.node_type = RDMA_NODE_IB_CA; + ibdev->ib_dev.local_dma_lkey = dev->caps.reserved_lkey; ibdev->ib_dev.phys_port_cnt = dev->caps.num_ports; ibdev->ib_dev.num_comp_vectors = 1; ibdev->ib_dev.dma_device = &dev->pdev->dev; @@ -627,6 +635,9 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) ibdev->ib_dev.get_dma_mr = mlx4_ib_get_dma_mr; ibdev->ib_dev.reg_user_mr = mlx4_ib_reg_user_mr; ibdev->ib_dev.dereg_mr = mlx4_ib_dereg_mr; + ibdev->ib_dev.alloc_fast_reg_mr = mlx4_ib_alloc_fast_reg_mr; + ibdev->ib_dev.alloc_fast_reg_page_list = mlx4_ib_alloc_fast_reg_page_list; + ibdev->ib_dev.free_fast_reg_page_list = mlx4_ib_free_fast_reg_page_list; ibdev->ib_dev.attach_mcast = mlx4_ib_mcg_attach; ibdev->ib_dev.detach_mcast = mlx4_ib_mcg_detach; ibdev->ib_dev.process_mad = mlx4_ib_process_mad; diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index c4cf5b69eefa..d26a91317d4d 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -83,6 +83,11 @@ struct mlx4_ib_mr { struct ib_umem *umem; }; +struct mlx4_ib_fast_reg_page_list { + struct ib_fast_reg_page_list ibfrpl; + dma_addr_t map; +}; + struct mlx4_ib_fmr { struct ib_fmr ibfmr; struct mlx4_fmr mfmr; @@ -199,6 +204,11 @@ static inline struct mlx4_ib_mr *to_mmr(struct ib_mr *ibmr) return container_of(ibmr, struct mlx4_ib_mr, ibmr); } +static inline struct mlx4_ib_fast_reg_page_list *to_mfrpl(struct ib_fast_reg_page_list *ibfrpl) +{ + return container_of(ibfrpl, struct mlx4_ib_fast_reg_page_list, ibfrpl); +} + static inline struct mlx4_ib_fmr *to_mfmr(struct ib_fmr *ibfmr) { return container_of(ibfmr, struct mlx4_ib_fmr, ibfmr); @@ -239,6 +249,11 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt_addr, int access_flags, struct ib_udata *udata); int mlx4_ib_dereg_mr(struct ib_mr *mr); +struct ib_mr *mlx4_ib_alloc_fast_reg_mr(struct ib_pd *pd, + int max_page_list_len); +struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device *ibdev, + int page_list_len); +void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list); int mlx4_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period); int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata); diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 68e92485fc76..db2086faa4ed 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -183,6 +183,76 @@ int mlx4_ib_dereg_mr(struct ib_mr *ibmr) return 0; } +struct ib_mr *mlx4_ib_alloc_fast_reg_mr(struct ib_pd *pd, + int max_page_list_len) +{ + struct mlx4_ib_dev *dev = to_mdev(pd->device); + struct mlx4_ib_mr *mr; + int err; + + mr = kmalloc(sizeof *mr, GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + err = mlx4_mr_alloc(dev->dev, to_mpd(pd)->pdn, 0, 0, 0, + max_page_list_len, 0, &mr->mmr); + if (err) + goto err_free; + + err = mlx4_mr_enable(dev->dev, &mr->mmr); + if (err) + goto err_mr; + + return &mr->ibmr; + +err_mr: + mlx4_mr_free(dev->dev, &mr->mmr); + +err_free: + kfree(mr); + return ERR_PTR(err); +} + +struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device *ibdev, + int page_list_len) +{ + struct mlx4_ib_dev *dev = to_mdev(ibdev); + struct mlx4_ib_fast_reg_page_list *mfrpl; + int size = page_list_len * sizeof (u64); + + if (size > PAGE_SIZE) + return ERR_PTR(-EINVAL); + + mfrpl = kmalloc(sizeof *mfrpl, GFP_KERNEL); + if (!mfrpl) + return ERR_PTR(-ENOMEM); + + mfrpl->ibfrpl.page_list = dma_alloc_coherent(&dev->dev->pdev->dev, + size, &mfrpl->map, + GFP_KERNEL); + if (!mfrpl->ibfrpl.page_list) + goto err_free; + + WARN_ON(mfrpl->map & 0x3f); + + return &mfrpl->ibfrpl; + +err_free: + kfree(mfrpl); + return ERR_PTR(-ENOMEM); +} + +void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list) +{ + struct mlx4_ib_dev *dev = to_mdev(page_list->device); + struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(page_list); + int size = page_list->max_page_list_len * sizeof (u64); + + dma_free_coherent(&dev->dev->pdev->dev, size, page_list->page_list, + mfrpl->map); + kfree(mfrpl); +} + struct ib_fmr *mlx4_ib_fmr_alloc(struct ib_pd *pd, int acc, struct ib_fmr_attr *fmr_attr) { diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index bda0859a5ac5..02a99bc4442e 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -78,6 +78,9 @@ static const __be32 mlx4_ib_opcode[] = { [IB_WR_RDMA_READ] = __constant_cpu_to_be32(MLX4_OPCODE_RDMA_READ), [IB_WR_ATOMIC_CMP_AND_SWP] = __constant_cpu_to_be32(MLX4_OPCODE_ATOMIC_CS), [IB_WR_ATOMIC_FETCH_AND_ADD] = __constant_cpu_to_be32(MLX4_OPCODE_ATOMIC_FA), + [IB_WR_SEND_WITH_INV] = __constant_cpu_to_be32(MLX4_OPCODE_SEND_INVAL), + [IB_WR_LOCAL_INV] = __constant_cpu_to_be32(MLX4_OPCODE_LOCAL_INVAL), + [IB_WR_FAST_REG_MR] = __constant_cpu_to_be32(MLX4_OPCODE_FMR), }; static struct mlx4_ib_sqp *to_msqp(struct mlx4_ib_qp *mqp) @@ -976,6 +979,10 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp, context->pd = cpu_to_be32(to_mpd(ibqp->pd)->pdn); context->params1 = cpu_to_be32(MLX4_IB_ACK_REQ_FREQ << 28); + /* Set "fast registration enabled" for all kernel QPs */ + if (!qp->ibqp.uobject) + context->params1 |= cpu_to_be32(1 << 11); + if (attr_mask & IB_QP_RNR_RETRY) { context->params1 |= cpu_to_be32(attr->rnr_retry << 13); optpar |= MLX4_QP_OPTPAR_RNR_RETRY; @@ -1322,6 +1329,38 @@ static int mlx4_wq_overflow(struct mlx4_ib_wq *wq, int nreq, struct ib_cq *ib_cq return cur + nreq >= wq->max_post; } +static __be32 convert_access(int acc) +{ + return (acc & IB_ACCESS_REMOTE_ATOMIC ? cpu_to_be32(MLX4_WQE_FMR_PERM_ATOMIC) : 0) | + (acc & IB_ACCESS_REMOTE_WRITE ? cpu_to_be32(MLX4_WQE_FMR_PERM_REMOTE_WRITE) : 0) | + (acc & IB_ACCESS_REMOTE_READ ? cpu_to_be32(MLX4_WQE_FMR_PERM_REMOTE_READ) : 0) | + (acc & IB_ACCESS_LOCAL_WRITE ? cpu_to_be32(MLX4_WQE_FMR_PERM_LOCAL_WRITE) : 0) | + cpu_to_be32(MLX4_WQE_FMR_PERM_LOCAL_READ); +} + +static void set_fmr_seg(struct mlx4_wqe_fmr_seg *fseg, struct ib_send_wr *wr) +{ + struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(wr->wr.fast_reg.page_list); + + fseg->flags = convert_access(wr->wr.fast_reg.access_flags); + fseg->mem_key = cpu_to_be32(wr->wr.fast_reg.rkey); + fseg->buf_list = cpu_to_be64(mfrpl->map); + fseg->start_addr = cpu_to_be64(wr->wr.fast_reg.iova_start); + fseg->reg_len = cpu_to_be64(wr->wr.fast_reg.length); + fseg->offset = 0; /* XXX -- is this just for ZBVA? */ + fseg->page_size = cpu_to_be32(wr->wr.fast_reg.page_shift); + fseg->reserved[0] = 0; + fseg->reserved[1] = 0; +} + +static void set_local_inv_seg(struct mlx4_wqe_local_inval_seg *iseg, u32 rkey) +{ + iseg->flags = 0; + iseg->mem_key = cpu_to_be32(rkey); + iseg->guest_id = 0; + iseg->pa = 0; +} + static __always_inline void set_raddr_seg(struct mlx4_wqe_raddr_seg *rseg, u64 remote_addr, u32 rkey) { @@ -1423,6 +1462,21 @@ static int build_lso_seg(struct mlx4_wqe_lso_seg *wqe, struct ib_send_wr *wr, return 0; } +static __be32 send_ieth(struct ib_send_wr *wr) +{ + switch (wr->opcode) { + case IB_WR_SEND_WITH_IMM: + case IB_WR_RDMA_WRITE_WITH_IMM: + return wr->ex.imm_data; + + case IB_WR_SEND_WITH_INV: + return cpu_to_be32(wr->ex.invalidate_rkey); + + default: + return 0; + } +} + int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, struct ib_send_wr **bad_wr) { @@ -1469,11 +1523,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, MLX4_WQE_CTRL_TCP_UDP_CSUM) : 0) | qp->sq_signal_bits; - if (wr->opcode == IB_WR_SEND_WITH_IMM || - wr->opcode == IB_WR_RDMA_WRITE_WITH_IMM) - ctrl->imm = wr->ex.imm_data; - else - ctrl->imm = 0; + ctrl->imm = send_ieth(wr); wqe += sizeof *ctrl; size = sizeof *ctrl / 16; @@ -1505,6 +1555,18 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, size += sizeof (struct mlx4_wqe_raddr_seg) / 16; break; + case IB_WR_LOCAL_INV: + set_local_inv_seg(wqe, wr->ex.invalidate_rkey); + wqe += sizeof (struct mlx4_wqe_local_inval_seg); + size += sizeof (struct mlx4_wqe_local_inval_seg) / 16; + break; + + case IB_WR_FAST_REG_MR: + set_fmr_seg(wqe, wr); + wqe += sizeof (struct mlx4_wqe_fmr_seg); + size += sizeof (struct mlx4_wqe_fmr_seg) / 16; + break; + default: /* No extra segments required for sends */ break; diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c index 0851ebdddfd4..57278224ba1e 100644 --- a/drivers/net/mlx4/fw.c +++ b/drivers/net/mlx4/fw.c @@ -202,7 +202,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_C_MPT_ENTRY_SZ_OFFSET 0x8e #define QUERY_DEV_CAP_MTT_ENTRY_SZ_OFFSET 0x90 #define QUERY_DEV_CAP_D_MPT_ENTRY_SZ_OFFSET 0x92 -#define QUERY_DEV_CAP_BMME_FLAGS_OFFSET 0x97 +#define QUERY_DEV_CAP_BMME_FLAGS_OFFSET 0x94 #define QUERY_DEV_CAP_RSVD_LKEY_OFFSET 0x98 #define QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET 0xa0 @@ -377,12 +377,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) } } - if (dev_cap->bmme_flags & 1) - mlx4_dbg(dev, "Base MM extensions: yes " - "(flags %d, rsvd L_Key %08x)\n", - dev_cap->bmme_flags, dev_cap->reserved_lkey); - else - mlx4_dbg(dev, "Base MM extensions: no\n"); + mlx4_dbg(dev, "Base MM extensions: flags %08x, rsvd L_Key %08x\n", + dev_cap->bmme_flags, dev_cap->reserved_lkey); /* * Each UAR has 4 EQ doorbells; so if a UAR is reserved, then diff --git a/drivers/net/mlx4/fw.h b/drivers/net/mlx4/fw.h index a0e046c149b7..fbf0e22be122 100644 --- a/drivers/net/mlx4/fw.h +++ b/drivers/net/mlx4/fw.h @@ -98,7 +98,7 @@ struct mlx4_dev_cap { int cmpt_entry_sz; int mtt_entry_sz; int resize_srq; - u8 bmme_flags; + u32 bmme_flags; u32 reserved_lkey; u64 max_icm_sz; int max_gso_sz; diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index d3736013fe9b..8e1d24cda1b0 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -158,6 +158,8 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.max_msg_sz = dev_cap->max_msg_sz; dev->caps.page_size_cap = ~(u32) (dev_cap->min_page_sz - 1); dev->caps.flags = dev_cap->flags; + dev->caps.bmme_flags = dev_cap->bmme_flags; + dev->caps.reserved_lkey = dev_cap->reserved_lkey; dev->caps.stat_rate_support = dev_cap->stat_rate_support; dev->caps.max_gso_sz = dev_cap->max_gso_sz; diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c index b3ea93b98689..a3c04c5f12c2 100644 --- a/drivers/net/mlx4/mr.c +++ b/drivers/net/mlx4/mr.c @@ -47,7 +47,7 @@ struct mlx4_mpt_entry { __be32 flags; __be32 qpn; __be32 key; - __be32 pd; + __be32 pd_flags; __be64 start; __be64 length; __be32 lkey; @@ -61,11 +61,15 @@ struct mlx4_mpt_entry { } __attribute__((packed)); #define MLX4_MPT_FLAG_SW_OWNS (0xfUL << 28) +#define MLX4_MPT_FLAG_FREE (0x3UL << 28) #define MLX4_MPT_FLAG_MIO (1 << 17) #define MLX4_MPT_FLAG_BIND_ENABLE (1 << 15) #define MLX4_MPT_FLAG_PHYSICAL (1 << 9) #define MLX4_MPT_FLAG_REGION (1 << 8) +#define MLX4_MPT_PD_FLAG_FAST_REG (1 << 26) +#define MLX4_MPT_PD_FLAG_EN_INV (3 << 24) + #define MLX4_MTT_FLAG_PRESENT 1 #define MLX4_MPT_STATUS_SW 0xF0 @@ -324,21 +328,30 @@ int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr) memset(mpt_entry, 0, sizeof *mpt_entry); - mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS | - MLX4_MPT_FLAG_MIO | + mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_MIO | MLX4_MPT_FLAG_REGION | mr->access); mpt_entry->key = cpu_to_be32(key_to_hw_index(mr->key)); - mpt_entry->pd = cpu_to_be32(mr->pd); + mpt_entry->pd_flags = cpu_to_be32(mr->pd | MLX4_MPT_PD_FLAG_EN_INV); mpt_entry->start = cpu_to_be64(mr->iova); mpt_entry->length = cpu_to_be64(mr->size); mpt_entry->entity_size = cpu_to_be32(mr->mtt.page_shift); + if (mr->mtt.order < 0) { mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_PHYSICAL); mpt_entry->mtt_seg = 0; - } else + } else { mpt_entry->mtt_seg = cpu_to_be64(mlx4_mtt_addr(dev, &mr->mtt)); + } + + if (mr->mtt.order >= 0 && mr->mtt.page_shift == 0) { + /* fast register MR in free state */ + mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_FREE); + mpt_entry->pd_flags |= cpu_to_be32(MLX4_MPT_PD_FLAG_FAST_REG); + } else { + mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS); + } err = mlx4_SW2HW_MPT(dev, mailbox, key_to_hw_index(mr->key) & (dev->caps.num_mpts - 1)); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 81b3dd5206e0..655ea0d1ee14 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -68,6 +68,14 @@ enum { MLX4_DEV_CAP_FLAG_UD_MCAST = 1 << 21 }; +enum { + MLX4_BMME_FLAG_LOCAL_INV = 1 << 6, + MLX4_BMME_FLAG_REMOTE_INV = 1 << 7, + MLX4_BMME_FLAG_TYPE_2_WIN = 1 << 9, + MLX4_BMME_FLAG_RESERVED_LKEY = 1 << 10, + MLX4_BMME_FLAG_FAST_REG_WR = 1 << 11, +}; + enum mlx4_event { MLX4_EVENT_TYPE_COMP = 0x00, MLX4_EVENT_TYPE_PATH_MIG = 0x01, @@ -184,6 +192,8 @@ struct mlx4_caps { u32 max_msg_sz; u32 page_size_cap; u32 flags; + u32 bmme_flags; + u32 reserved_lkey; u16 stat_rate_support; u8 port_width_cap[MLX4_MAX_PORTS + 1]; int max_gso_sz; diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index f02e9ed36cfa..e27082cd650e 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -233,6 +233,14 @@ struct mlx4_wqe_bind_seg { __be64 length; }; +enum { + MLX4_WQE_FMR_PERM_LOCAL_READ = 1 << 27, + MLX4_WQE_FMR_PERM_LOCAL_WRITE = 1 << 28, + MLX4_WQE_FMR_PERM_REMOTE_READ = 1 << 29, + MLX4_WQE_FMR_PERM_REMOTE_WRITE = 1 << 30, + MLX4_WQE_FMR_PERM_ATOMIC = 1 << 31 +}; + struct mlx4_wqe_fmr_seg { __be32 flags; __be32 mem_key; @@ -255,11 +263,11 @@ struct mlx4_wqe_fmr_ext_seg { }; struct mlx4_wqe_local_inval_seg { - u8 flags; - u8 reserved1[3]; + __be32 flags; + u32 reserved1; __be32 mem_key; - u8 reserved2[3]; - u8 guest_id; + u32 reserved2[2]; + __be32 guest_id; __be64 pa; }; -- cgit From c97c6aca75fd5f718056fde7cff798b8cbdb07c0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:50 +0200 Subject: ide: pass hw_regs_t-s to ide_device_add[_all]() (take 3) * Add 'hw_regs_t **hws' argument to ide_device_add[_all]() and convert host drivers + ide_legacy_init_one() + ide_setup_pci_device[s]() to use it instead of calling ide_init_port_hw() directly. [ However if host has > 1 port we must still set hwif->chipset to hint consecutive ide_find_port() call that the previous slot is occupied. ] * Unexport ide_init_port_hw(). v2: * Use defines instead of hard-coded values in buddha.c, gayle.c and q40ide.c. (Suggested by Geert Uytterhoeven) * Better patch description. v3: * Fix build problem in ide-cs.c. (Noticed by Stephen Rothwell) There should be no functional changes caused by this patch. Cc: Geert Uytterhoeven Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 13 ++++++------- drivers/ide/arm/ide_arm.c | 5 ++--- drivers/ide/arm/palm_bk3710.c | 6 ++---- drivers/ide/arm/rapide.c | 5 ++--- drivers/ide/h8300/ide-h8300.c | 5 ++--- drivers/ide/ide-generic.c | 23 ++++++++++++----------- drivers/ide/ide-pnp.c | 6 ++---- drivers/ide/ide-probe.c | 29 ++++++++++++++++++----------- drivers/ide/ide.c | 1 - drivers/ide/legacy/buddha.c | 16 +++++++++------- drivers/ide/legacy/falconide.c | 6 ++---- drivers/ide/legacy/gayle.c | 13 ++++++------- drivers/ide/legacy/ide-4drives.c | 10 ++++++---- drivers/ide/legacy/ide-cs.c | 10 +++------- drivers/ide/legacy/ide_platform.c | 9 +++------ drivers/ide/legacy/macide.c | 6 ++---- drivers/ide/legacy/q40ide.c | 14 ++++++-------- drivers/ide/mips/au1xxx-ide.c | 6 ++---- drivers/ide/mips/swarm.c | 6 ++---- drivers/ide/pci/cmd640.c | 10 ++++++---- drivers/ide/pci/cs5520.c | 5 +++-- drivers/ide/pci/delkin_cb.c | 6 ++---- drivers/ide/pci/scc_pata.c | 5 ++--- drivers/ide/pci/sgiioc4.c | 6 ++---- drivers/ide/ppc/pmac.c | 5 ++--- drivers/ide/setup-pci.c | 38 +++++++++++++++++++++++--------------- include/linux/ide.h | 7 ++++--- 27 files changed, 131 insertions(+), 140 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 52f58c885783..850fe9342a1f 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -442,8 +442,8 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) { ide_hwif_t *hwif; void __iomem *base; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); if (!base) @@ -467,7 +467,6 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) if (!hwif) return -ENODEV; - ide_init_port_hw(hwif, &hw); default_hwif_mmiops(hwif); state->hwif[0] = hwif; @@ -476,7 +475,7 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) idx[0] = hwif->index; - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); return 0; } @@ -497,9 +496,9 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) void __iomem *ioc_base, *easi_base; unsigned int sel = 0; int ret; + hw_regs_t hw[2], *hws[] = { &hw[0], NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = icside_v6_port_info; - hw_regs_t hw[2]; ioc_base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); if (!ioc_base) { @@ -545,16 +544,16 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) if (hwif == NULL) return -ENODEV; - ide_init_port_hw(hwif, &hw[0]); + hwif->chipset = ide_acorn; default_hwif_mmiops(hwif); idx[0] = hwif->index; mate = ide_find_port(); if (mate) { - ide_init_port_hw(mate, &hw[1]); default_hwif_mmiops(mate); + hws[1] = &hw[1]; idx[1] = mate->index; } @@ -569,7 +568,7 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) d.dma_ops = NULL; } - ide_device_add(idx, &d); + ide_device_add(idx, &d, hws); return 0; diff --git a/drivers/ide/arm/ide_arm.c b/drivers/ide/arm/ide_arm.c index 2f311da4c963..e9831bbd988a 100644 --- a/drivers/ide/arm/ide_arm.c +++ b/drivers/ide/arm/ide_arm.c @@ -29,8 +29,8 @@ static int __init ide_arm_init(void) { ide_hwif_t *hwif; - hw_regs_t hw; unsigned long base = IDE_ARM_IO, ctl = IDE_ARM_IO + 0x206; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!request_region(base, 8, DRV_NAME)) { @@ -53,10 +53,9 @@ static int __init ide_arm_init(void) hwif = ide_find_port(); if (hwif) { - ide_init_port_hw(hwif, &hw); idx[0] = hwif->index; - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); } return 0; diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index c79b85b6e4a3..023c10753f15 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -351,7 +351,7 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) ide_hwif_t *hwif; unsigned long base, rate; int i; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; clk = clk_get(NULL, "IDECLK"); @@ -400,13 +400,11 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) i = hwif->index; - ide_init_port_hw(hwif, &hw); - default_hwif_mmiops(hwif); idx[0] = i; - ide_device_add(idx, &palm_bk3710_port_info); + ide_device_add(idx, &palm_bk3710_port_info, hws); return 0; out: diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index 43057e0303c8..01896f6e8acf 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -35,8 +35,8 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) ide_hwif_t *hwif; void __iomem *base; int ret; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; ret = ecard_request_resources(ec); if (ret) @@ -59,12 +59,11 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) goto release; } - ide_init_port_hw(hwif, &hw); default_hwif_mmiops(hwif); idx[0] = hwif->index; - ide_device_add(idx, &rapide_port_info); + ide_device_add(idx, &rapide_port_info, hws); ecard_set_drvdata(ec, hwif); goto out; diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 20fad6d542cc..a71433b3d0f3 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -184,9 +184,9 @@ static const struct ide_port_info h8300_port_info = { static int __init h8300_ide_init(void) { - hw_regs_t hw; ide_hwif_t *hwif; int index; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n"); @@ -205,12 +205,11 @@ static int __init h8300_ide_init(void) return -ENOENT; index = hwif->index; - ide_init_port_hw(hwif, &hw); hwif_setup(hwif); idx[0] = index; - ide_device_add(idx, &h8300_port_info); + ide_device_add(idx, &h8300_port_info, hws); return 0; diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index 2d92214096ab..bb9fc905d60c 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -31,7 +31,7 @@ static ssize_t store_add(struct class *cls, const char *buf, size_t n) ide_hwif_t *hwif; unsigned int base, ctl; int irq; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[] = { 0xff, 0xff, 0xff, 0xff }; if (sscanf(buf, "%x:%x:%d", &base, &ctl, &irq) != 3) @@ -46,11 +46,9 @@ static ssize_t store_add(struct class *cls, const char *buf, size_t n) hw.irq = irq; hw.chipset = ide_generic; - ide_init_port_hw(hwif, &hw); - idx[0] = hwif->index; - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); return n; }; @@ -90,6 +88,7 @@ static int __init ide_generic_sysfs_init(void) static int __init ide_generic_init(void) { + hw_regs_t hw[MAX_HWIFS], *hws[MAX_HWIFS]; u8 idx[MAX_HWIFS]; int i; @@ -99,8 +98,8 @@ static int __init ide_generic_init(void) for (i = 0; i < MAX_HWIFS; i++) { ide_hwif_t *hwif; unsigned long io_addr = ide_default_io_base(i); - hw_regs_t hw; + hws[i] = NULL; idx[i] = 0xff; if ((probe_mask & (1 << i)) && io_addr) { @@ -129,17 +128,19 @@ static int __init ide_generic_init(void) continue; } - memset(&hw, 0, sizeof(hw)); - ide_std_init_ports(&hw, io_addr, io_addr + 0x206); - hw.irq = ide_default_irq(io_addr); - hw.chipset = ide_generic; - ide_init_port_hw(hwif, &hw); + hwif->chipset = ide_generic; + + memset(&hw[i], 0, sizeof(hw[i])); + ide_std_init_ports(&hw[i], io_addr, io_addr + 0x206); + hw[i].irq = ide_default_irq(io_addr); + hw[i].chipset = ide_generic; + hws[i] = &hw[i]; idx[i] = i; } } - ide_device_add_all(idx, NULL); + ide_device_add_all(idx, NULL, hws); if (ide_generic_sysfs_init()) printk(KERN_ERR DRV_NAME ": failed to create ide_generic " diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index 03f2ef5470a3..89cd5cbe8573 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -29,9 +29,9 @@ static struct pnp_device_id idepnp_devices[] = { static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { - hw_regs_t hw; ide_hwif_t *hwif; unsigned long base, ctl; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; printk(KERN_INFO DRV_NAME ": generic PnP IDE interface\n"); @@ -64,11 +64,9 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) u8 index = hwif->index; u8 idx[4] = { index, 0xff, 0xff, 0xff }; - ide_init_port_hw(hwif, &hw); - pnp_set_drvdata(dev, hwif); - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); return 0; } diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 235ebdb29b28..9d8686a49fcf 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1501,7 +1501,7 @@ out_found: } EXPORT_SYMBOL_GPL(ide_find_port_slot); -int ide_device_add_all(u8 *idx, const struct ide_port_info *d) +int ide_device_add_all(u8 *idx, const struct ide_port_info *d, hw_regs_t **hws) { ide_hwif_t *hwif, *mate = NULL; int i, rc = 0; @@ -1514,6 +1514,7 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d) hwif = &ide_hwifs[idx[i]]; + ide_init_port_hw(hwif, hws[i]); ide_port_apply_params(hwif); if (d == NULL) { @@ -1603,15 +1604,18 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d) } EXPORT_SYMBOL_GPL(ide_device_add_all); -int ide_device_add(u8 idx[4], const struct ide_port_info *d) +int ide_device_add(u8 *idx, const struct ide_port_info *d, hw_regs_t **hws) { + hw_regs_t *hws_all[MAX_HWIFS]; u8 idx_all[MAX_HWIFS]; int i; - for (i = 0; i < MAX_HWIFS; i++) + for (i = 0; i < MAX_HWIFS; i++) { + hws_all[i] = (i < 4) ? hws[i] : NULL; idx_all[i] = (i < 4) ? idx[i] : 0xff; + } - return ide_device_add_all(idx_all, d); + return ide_device_add_all(idx_all, d, hws_all); } EXPORT_SYMBOL_GPL(ide_device_add); @@ -1634,8 +1638,8 @@ void ide_port_scan(ide_hwif_t *hwif) } EXPORT_SYMBOL_GPL(ide_port_scan); -static void ide_legacy_init_one(u8 *idx, hw_regs_t *hw, u8 port_no, - const struct ide_port_info *d, +static void ide_legacy_init_one(u8 *idx, hw_regs_t **hws, hw_regs_t *hw, + u8 port_no, const struct ide_port_info *d, unsigned long config) { ide_hwif_t *hwif; @@ -1671,9 +1675,12 @@ static void ide_legacy_init_one(u8 *idx, hw_regs_t *hw, u8 port_no, hwif = ide_find_port_slot(d); if (hwif) { - ide_init_port_hw(hwif, hw); + hwif->chipset = hw->chipset; + if (config) hwif->config_data = config; + + hws[port_no] = hw; idx[port_no] = hwif->index; } } @@ -1681,19 +1688,19 @@ static void ide_legacy_init_one(u8 *idx, hw_regs_t *hw, u8 port_no, int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config) { u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw[2]; + hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; memset(&hw, 0, sizeof(hw)); if ((d->host_flags & IDE_HFLAG_QD_2ND_PORT) == 0) - ide_legacy_init_one(idx, &hw[0], 0, d, config); - ide_legacy_init_one(idx, &hw[1], 1, d, config); + ide_legacy_init_one(idx, hws, &hw[0], 0, d, config); + ide_legacy_init_one(idx, hws, &hw[1], 1, d, config); if (idx[0] == 0xff && idx[1] == 0xff && (d->host_flags & IDE_HFLAG_SINGLE)) return -ENOENT; - ide_device_add(idx, d); + ide_device_add(idx, d, hws); return 0; } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index d4a6b102a772..b6018f7b0907 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -288,7 +288,6 @@ void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) hwif->gendev.parent = hw->parent ? hw->parent : hw->dev; hwif->ack_intr = hw->ack_intr; } -EXPORT_SYMBOL_GPL(ide_init_port_hw); /* * Locks for IDE setting functionality diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c index 0497e7f85b09..c61bc6a1db36 100644 --- a/drivers/ide/legacy/buddha.c +++ b/drivers/ide/legacy/buddha.c @@ -37,6 +37,8 @@ #define CATWEASEL_NUM_HWIFS 3 #define XSURF_NUM_HWIFS 2 +#define MAX_NUM_HWIFS 3 + /* * Bases of the IDE interfaces (relative to the board address) */ @@ -148,7 +150,6 @@ static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base, static int __init buddha_init(void) { - hw_regs_t hw; ide_hwif_t *hwif; int i; @@ -159,6 +160,7 @@ static int __init buddha_init(void) while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { unsigned long board; + hw_regs_t hw[MAX_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) { @@ -221,19 +223,19 @@ fail_base2: ack_intr = xsurf_ack_intr; } - buddha_setup_ports(&hw, base, ctl, irq_port, ack_intr); + buddha_setup_ports(&hw[i], base, ctl, irq_port, + ack_intr); hwif = ide_find_port(); if (hwif) { - u8 index = hwif->index; - - ide_init_port_hw(hwif, &hw); + hwif->chipset = ide_generic; - idx[i] = index; + hws[i] = &hw[i]; + idx[i] = hwif->index; } } - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); } return 0; diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 129a812bb57f..1bb2aa72cc7f 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -91,8 +91,8 @@ static void __init falconide_setup_ports(hw_regs_t *hw) static int __init falconide_init(void) { - hw_regs_t hw; ide_hwif_t *hwif; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE)) return 0; @@ -111,14 +111,12 @@ static int __init falconide_init(void) u8 index = hwif->index; u8 idx[4] = { index, 0xff, 0xff, 0xff }; - ide_init_port_hw(hwif, &hw); - /* Atari has a byte-swapped IDE interface */ hwif->input_data = falconide_input_data; hwif->output_data = falconide_output_data; ide_get_lock(NULL, NULL); - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); ide_release_lock(); } diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index 7e74b20202df..e45c7341186c 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -125,6 +125,7 @@ static void __init gayle_setup_ports(hw_regs_t *hw, unsigned long base, static int __init gayle_init(void) { int a4000, i; + hw_regs_t hw[GAYLE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!MACH_IS_AMIGA) @@ -151,7 +152,6 @@ found: for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) { unsigned long base, ctrlport, irqport; ide_ack_intr_t *ack_intr; - hw_regs_t hw; ide_hwif_t *hwif; unsigned long phys_base, res_start, res_n; @@ -179,20 +179,19 @@ found: base = (unsigned long)ZTWO_VADDR(phys_base); ctrlport = GAYLE_HAS_CONTROL_REG ? (base + GAYLE_CONTROL) : 0; - gayle_setup_ports(&hw, base, ctrlport, irqport, ack_intr); + gayle_setup_ports(&hw[i], base, ctrlport, irqport, ack_intr); hwif = ide_find_port(); if (hwif) { - u8 index = hwif->index; + hwif->chipset = ide_generic; - ide_init_port_hw(hwif, &hw); - - idx[i] = index; + hws[i] = &hw[i]; + idx[i] = hwif->index; } else release_mem_region(res_start, res_n); } - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); return 0; } diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c index 89c8ff0a4d08..6310dc50e3c5 100644 --- a/drivers/ide/legacy/ide-4drives.c +++ b/drivers/ide/legacy/ide-4drives.c @@ -30,8 +30,8 @@ static int __init ide_4drives_init(void) { ide_hwif_t *hwif, *mate; unsigned long base = 0x1f0, ctl = 0x3f6; + hw_regs_t hw, *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; if (probe_4drives == 0) return -ENODEV; @@ -57,17 +57,19 @@ static int __init ide_4drives_init(void) hwif = ide_find_port(); if (hwif) { - ide_init_port_hw(hwif, &hw); + hwif->chipset = ide_4drives; + + hws[0] = &hw; idx[0] = hwif->index; } mate = ide_find_port(); if (mate) { - ide_init_port_hw(mate, &hw); + hws[1] = &hw; idx[1] = mate->index; } - ide_device_add(idx, &ide_4drives_port_info); + ide_device_add(idx, &ide_4drives_port_info, hws); return 0; } diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index 27b1e0b7ecb4..f93d5454ebf8 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -161,8 +161,8 @@ static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl, unsigned long irq, struct pcmcia_device *handle) { ide_hwif_t *hwif; - hw_regs_t hw; int i; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!request_region(io, 8, DRV_NAME)) { @@ -188,13 +188,9 @@ static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl, if (hwif == NULL) goto out_release; - i = hwif->index; + idx[0] = hwif->index; - ide_init_port_hw(hwif, &hw); - - idx[0] = i; - - ide_device_add(idx, &idecs_port_info); + ide_device_add(idx, &idecs_port_info, hws); if (hwif->present) return hwif; diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index a249562b34b5..609da0d43196 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -54,10 +54,9 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) void __iomem *base, *alt_base; ide_hwif_t *hwif; struct pata_platform_info *pdata; + int ret = 0, mmio = 0; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - int ret = 0; - int mmio = 0; - hw_regs_t hw; struct ide_port_info d = platform_ide_port_info; pdata = pdev->dev.platform_data; @@ -104,8 +103,6 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start); hw.dev = &pdev->dev; - ide_init_port_hw(hwif, &hw); - if (mmio) { d.host_flags |= IDE_HFLAG_MMIO; default_hwif_mmiops(hwif); @@ -113,7 +110,7 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) idx[0] = hwif->index; - ide_device_add(idx, &d); + ide_device_add(idx, &d, hws); platform_set_drvdata(pdev, hwif); diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c index 0a6195bcfeda..d839df2239fc 100644 --- a/drivers/ide/legacy/macide.c +++ b/drivers/ide/legacy/macide.c @@ -95,7 +95,7 @@ static int __init macide_init(void) ide_ack_intr_t *ack_intr; unsigned long base; int irq; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!MACH_IS_MAC) return -ENODEV; @@ -130,9 +130,7 @@ static int __init macide_init(void) u8 index = hwif->index; u8 idx[4] = { index, 0xff, 0xff, 0xff }; - ide_init_port_hw(hwif, &hw); - - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); } return 0; diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index 9c2b9d078f69..fcb04b8b0238 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -112,7 +112,7 @@ static int __init q40ide_init(void) { int i; ide_hwif_t *hwif; - const char *name; + hw_regs_t hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!MACH_IS_Q40) @@ -121,9 +121,8 @@ static int __init q40ide_init(void) printk(KERN_INFO "ide: Q40 IDE controller\n"); for (i = 0; i < Q40IDE_NUM_HWIFS; i++) { - hw_regs_t hw; + const char *name = q40_ide_names[i]; - name = q40_ide_names[i]; if (!request_region(pcide_bases[i], 8, name)) { printk("could not reserve ports %lx-%lx for %s\n", pcide_bases[i],pcide_bases[i]+8,name); @@ -135,24 +134,23 @@ static int __init q40ide_init(void) release_region(pcide_bases[i], 8); continue; } - q40_ide_setup_ports(&hw, pcide_bases[i], - NULL, -// m68kide_iops, + q40_ide_setup_ports(&hw[i], pcide_bases[i], NULL, q40ide_default_irq(pcide_bases[i])); hwif = ide_find_port(); if (hwif) { - ide_init_port_hw(hwif, &hw); + hwif->chipset = ide_generic; /* Q40 has a byte-swapped IDE interface */ hwif->input_data = q40ide_input_data; hwif->output_data = q40ide_output_data; + hws[i] = &hw[i]; idx[i] = hwif->index; } } - ide_device_add(idx, NULL); + ide_device_add(idx, NULL, hws); return 0; } diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index 48d57cae63c6..475da582fd89 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -546,8 +546,8 @@ static int au_ide_probe(struct device *dev) ide_hwif_t *hwif; struct resource *res; int ret = 0; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; #if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA) char *mode = "MWDMA2"; @@ -596,8 +596,6 @@ static int au_ide_probe(struct device *dev) hw.dev = dev; hw.chipset = ide_au1xxx; - ide_init_port_hw(hwif, &hw); - /* If the user has selected DDMA assisted copies, then set up a few local I/O function entry points */ @@ -611,7 +609,7 @@ static int au_ide_probe(struct device *dev) idx[0] = hwif->index; - ide_device_add(idx, &au1xxx_port_info); + ide_device_add(idx, &au1xxx_port_info, hws); dev_set_drvdata(dev, hwif); diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index 9f1212cc4aed..6da6844d2d8c 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -75,8 +75,8 @@ static int __devinit swarm_ide_probe(struct device *dev) ide_hwif_t *hwif; u8 __iomem *base; phys_t offset, size; - hw_regs_t hw; int i; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[] = { 0xff, 0xff, 0xff, 0xff }; if (!SIBYTE_HAVE_IDE) @@ -120,14 +120,12 @@ static int __devinit swarm_ide_probe(struct device *dev) if (hwif == NULL) goto err; - ide_init_port_hw(hwif, &hw); - /* Setup MMIO ops. */ default_hwif_mmiops(hwif); idx[0] = hwif->index; - ide_device_add(idx, &swarm_port_info); + ide_device_add(idx, &swarm_port_info, hws); dev_set_drvdata(dev, hwif); diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index 1ad1e23e3105..ccde1e444e13 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -717,8 +717,8 @@ static int __init cmd640x_init(void) int second_port_cmd640 = 0, rc; const char *bus_type, *port2; u8 b, cfr; + hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw[2]; if (cmd640_vlb && probe_for_cmd640_vlb()) { bus_type = "VLB"; @@ -787,7 +787,9 @@ static int __init cmd640x_init(void) * Initialize data for primary port */ if (cmd_hwif0) { - ide_init_port_hw(cmd_hwif0, &hw[0]); + cmd_hwif0->chipset = ide_cmd640; + + hws[0] = &hw[0]; idx[0] = cmd_hwif0->index; } @@ -832,7 +834,7 @@ static int __init cmd640x_init(void) if (second_port_cmd640) { cmd_hwif1 = ide_find_port(); if (cmd_hwif1) { - ide_init_port_hw(cmd_hwif1, &hw[1]); + hws[1] = &hw[1]; idx[1] = cmd_hwif1->index; } } @@ -843,7 +845,7 @@ static int __init cmd640x_init(void) cmd640_dump_regs(); #endif - ide_device_add(idx, &cmd640_port_info); + ide_device_add(idx, &cmd640_port_info, hws); return 1; } diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index 992b1cf8db69..a13f2bc0665c 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -146,6 +146,7 @@ static const struct ide_port_info cyrix_chipsets[] __devinitdata = { static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) { const struct ide_port_info *d = &cyrix_chipsets[id->driver_data]; + hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; ide_setup_pci_noise(dev, d); @@ -168,9 +169,9 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic * do all the device setup for us */ - ide_pci_setup_ports(dev, d, 14, &idx[0]); + ide_pci_setup_ports(dev, d, 14, &idx[0], &hw[0], &hws[0]); - ide_device_add(idx, d); + ide_device_add(idx, d, hws); return 0; } diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c index 0106e2a2df77..33fe15db408a 100644 --- a/drivers/ide/pci/delkin_cb.c +++ b/drivers/ide/pci/delkin_cb.c @@ -57,9 +57,9 @@ static int __devinit delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) { unsigned long base; - hw_regs_t hw; ide_hwif_t *hwif = NULL; int i, rc; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; rc = pci_enable_device(dev); @@ -93,11 +93,9 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) i = hwif->index; - ide_init_port_hw(hwif, &hw); - idx[0] = i; - ide_device_add(idx, &delkin_cb_port_info); + ide_device_add(idx, &delkin_cb_port_info, hws); pci_set_drvdata(dev, hwif); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 789c66dfbde5..328e2df66550 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -554,7 +554,7 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, { struct scc_ports *ports = pci_get_drvdata(dev); ide_hwif_t *hwif = NULL; - hw_regs_t hw; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; int i; @@ -568,11 +568,10 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, hw.irq = dev->irq; hw.dev = &dev->dev; hw.chipset = ide_pci; - ide_init_port_hw(hwif, &hw); idx[0] = hwif->index; - ide_device_add(idx, d); + ide_device_add(idx, d, hws); return 0; } diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index c79ff5b41088..76afa1f9c599 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -584,8 +584,8 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) unsigned long bar0, cmd_phys_base, ctl; void __iomem *virt_base; ide_hwif_t *hwif; + hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; - hw_regs_t hw; struct ide_port_info d = sgiioc4_port_info; /* Get the CmdBlk and CtrlBlk Base Registers */ @@ -622,8 +622,6 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) if (hwif == NULL) goto err; - ide_init_port_hw(hwif, &hw); - /* The IOC4 uses MMIO rather than Port IO. */ default_hwif_mmiops(hwif); @@ -634,7 +632,7 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) idx[0] = hwif->index; - if (ide_device_add(idx, &d)) + if (ide_device_add(idx, &d, hws)) return -EIO; return 0; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index 4e2944ee76d8..e68e33bb2c35 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1010,6 +1010,7 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) struct device_node *np = pmif->node; const int *bidp; ide_hwif_t *hwif; + hw_regs_t *hws[] = { hw, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = pmac_port_info; @@ -1095,11 +1096,9 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) default_hwif_mmiops(hwif); hwif->OUTBSYNC = pmac_outbsync; - ide_init_port_hw(hwif, hw); - idx[0] = hwif->index; - ide_device_add(idx, &d); + ide_device_add(idx, &d, hws); return 0; } diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index 7ae6ae45331f..acb467c6f345 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -289,6 +289,7 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info * * @d: IDE port info * @port: port number * @irq: PCI IRQ + * @hw: hw_regs_t instance corresponding to this port * * Perform the initial set up for the hardware interface structure. This * is done per interface port rather than per PCI device. There may be @@ -299,11 +300,11 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info * static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, const struct ide_port_info *d, - unsigned int port, int irq) + unsigned int port, int irq, + hw_regs_t *hw) { unsigned long ctl = 0, base = 0; ide_hwif_t *hwif; - struct hw_regs_s hw; if ((d->host_flags & IDE_HFLAG_ISA_PORTS) == 0) { if (ide_pci_check_iomem(dev, d, 2 * port) || @@ -327,17 +328,17 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, return NULL; } + memset(hw, 0, sizeof(*hw)); + hw->irq = irq; + hw->dev = &dev->dev; + hw->chipset = d->chipset ? d->chipset : ide_pci; + ide_std_init_ports(hw, base, ctl | 2); + hwif = ide_find_port_slot(d); if (hwif == NULL) return NULL; - memset(&hw, 0, sizeof(hw)); - hw.irq = irq; - hw.dev = &dev->dev; - hw.chipset = d->chipset ? d->chipset : ide_pci; - ide_std_init_ports(&hw, base, ctl | 2); - - ide_init_port_hw(hwif, &hw); + hwif->chipset = hw->chipset; return hwif; } @@ -430,6 +431,8 @@ out: * @d: IDE port info * @pciirq: IRQ line * @idx: ATA index table to update + * @hw: hw_regs_t instances corresponding to this PCI IDE device + * @hws: hw_regs_t pointers table to update * * Scan the interfaces attached to this device and do any * necessary per port setup. Attach the devices and ask the @@ -440,7 +443,8 @@ out: * where the chipset setup is not the default PCI IDE one. */ -void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, int pciirq, u8 *idx) +void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, + int pciirq, u8 *idx, hw_regs_t *hw, hw_regs_t **hws) { int channels = (d->host_flags & IDE_HFLAG_SINGLE) ? 1 : 2, port; ide_hwif_t *hwif; @@ -459,10 +463,11 @@ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, int continue; /* port not enabled */ } - hwif = ide_hwif_configure(dev, d, port, pciirq); + hwif = ide_hwif_configure(dev, d, port, pciirq, hw + port); if (hwif == NULL) continue; + *(hws + port) = hw + port; *(idx + port) = hwif->index; } } @@ -537,15 +542,16 @@ out: int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) { u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; int ret; ret = do_ide_setup_pci_device(dev, d, 1); if (ret >= 0) { /* FIXME: silent failure can happen */ - ide_pci_setup_ports(dev, d, ret, &idx[0]); + ide_pci_setup_ports(dev, d, ret, &idx[0], &hw[0], &hws[0]); - ide_device_add(idx, d); + ide_device_add(idx, d, hws); } return ret; @@ -557,6 +563,7 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, { struct pci_dev *pdev[] = { dev1, dev2 }; int ret, i; + hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; for (i = 0; i < 2; i++) { @@ -570,10 +577,11 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, goto out; /* FIXME: silent failure can happen */ - ide_pci_setup_ports(pdev[i], d, ret, &idx[i*2]); + ide_pci_setup_ports(pdev[i], d, ret, &idx[i*2], &hw[i*2], + &hws[i*2]); } - ide_device_add(idx, d); + ide_device_add(idx, d, hws); out: return ret; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 4726126f5a59..f58548becac0 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1000,7 +1000,8 @@ extern int __ide_pci_register_driver(struct pci_driver *driver, struct module *o #define ide_pci_register_driver(d) pci_register_driver(d) #endif -void ide_pci_setup_ports(struct pci_dev *, const struct ide_port_info *, int, u8 *); +void ide_pci_setup_ports(struct pci_dev *, const struct ide_port_info *, int, + u8 *, hw_regs_t *, hw_regs_t **); void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *); #ifdef CONFIG_BLK_DEV_IDEDMA_PCI @@ -1217,8 +1218,8 @@ void ide_undecoded_slave(ide_drive_t *); void ide_port_apply_params(ide_hwif_t *); -int ide_device_add_all(u8 *idx, const struct ide_port_info *); -int ide_device_add(u8 idx[4], const struct ide_port_info *); +int ide_device_add_all(u8 *, const struct ide_port_info *, hw_regs_t **); +int ide_device_add(u8 *, const struct ide_port_info *, hw_regs_t **); int ide_legacy_device_add(const struct ide_port_info *, unsigned long); void ide_port_unregister_devices(ide_hwif_t *); void ide_port_scan(ide_hwif_t *); -- cgit From b2f951aabc9cc7d5fb987aeec9aef96ccce618a5 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:50 +0200 Subject: ide: add ->read_sff_dma_status method Add ->read_sff_dma_status method for reading DMA Status register and use it instead of ->INB. While at it: * Use inb() directly in ns87415.c::ns87415_dma_end(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-dma.c | 12 ++++++------ drivers/ide/ide-iops.c | 10 ++++++++++ drivers/ide/pci/ns87415.c | 13 ++++++++++--- drivers/ide/pci/scc_pata.c | 7 +++++++ include/linux/ide.h | 2 ++ 5 files changed, 35 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 7ee44f86bc54..ebddedde24af 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -334,7 +334,7 @@ static int config_drive_for_dma (ide_drive_t *drive) static int dma_timer_expiry (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = hwif->read_sff_dma_status(hwif); printk(KERN_WARNING "%s: dma_timer_expiry: dma status == 0x%02x\n", drive->name, dma_stat); @@ -369,7 +369,7 @@ void ide_dma_host_set(ide_drive_t *drive, int on) { ide_hwif_t *hwif = HWIF(drive); u8 unit = (drive->select.b.unit & 0x01); - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = hwif->read_sff_dma_status(hwif); if (on) dma_stat |= (1 << (5 + unit)); @@ -472,8 +472,8 @@ int ide_dma_setup(ide_drive_t *drive) /* specify r/w */ hwif->OUTB(reading, hwif->dma_command); - /* read dma_status for INTR & ERROR flags */ - dma_stat = hwif->INB(hwif->dma_status); + /* read DMA status for INTR & ERROR flags */ + dma_stat = hwif->read_sff_dma_status(hwif); /* clear INTR & ERROR flags */ hwif->OUTB(dma_stat|6, hwif->dma_status); @@ -520,7 +520,7 @@ int __ide_dma_end (ide_drive_t *drive) /* stop DMA */ hwif->OUTB(dma_cmd&~1, hwif->dma_command); /* get DMA status */ - dma_stat = hwif->INB(hwif->dma_status); + dma_stat = hwif->read_sff_dma_status(hwif); /* clear the INTR & ERROR bits */ hwif->OUTB(dma_stat|6, hwif->dma_status); /* purge DMA mappings */ @@ -537,7 +537,7 @@ EXPORT_SYMBOL(__ide_dma_end); int ide_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = hwif->read_sff_dma_status(hwif); /* return 1 if INTR asserted */ if ((dma_stat & 4) == 4) diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 44aaec256a30..a09bf4369ed8 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -103,6 +103,14 @@ void SELECT_MASK(ide_drive_t *drive, int mask) port_ops->maskproc(drive, mask); } +static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->dma_status); + else + return inb(hwif->dma_status); +} + static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; @@ -323,6 +331,8 @@ static void ata_output_data(ide_drive_t *drive, struct request *rq, void default_hwif_transport(ide_hwif_t *hwif) { + hwif->read_sff_dma_status = ide_read_sff_dma_status; + hwif->tf_load = ide_tf_load; hwif->tf_read = ide_tf_read; diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 45ba71a7182f..9ffdbb89df5c 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -63,6 +63,11 @@ static u8 superio_ide_inb (unsigned long port) return inb(port); } +static u8 superio_read_sff_dma_status(ide_hwif_t *hwif) +{ + return superio_ide_inb(hwif->dma_status); +} + static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) { struct ide_io_ports *io_ports = &drive->hwif->io_ports; @@ -122,6 +127,8 @@ static void __devinit superio_ide_init_iops (struct hwif_s *hwif) tmp = superio_ide_inb(superio_ide_dma_status[port]); outb(tmp | 0x66, superio_ide_dma_status[port]); + hwif->read_sff_dma_status = superio_read_sff_dma_status; + hwif->tf_read = superio_tf_read; /* We need to override inb to workaround a SuperIO errata */ @@ -200,13 +207,13 @@ static int ns87415_dma_end(ide_drive_t *drive) u8 dma_stat = 0, dma_cmd = 0; drive->waiting_for_dma = 0; - dma_stat = hwif->INB(hwif->dma_status); + dma_stat = hwif->read_sff_dma_status(hwif); /* get dma command mode */ - dma_cmd = hwif->INB(hwif->dma_command); + dma_cmd = inb(hwif->dma_command); /* stop DMA */ outb(dma_cmd & ~1, hwif->dma_command); /* from ERRATA: clear the INTR & ERROR bits */ - dma_cmd = hwif->INB(hwif->dma_command); + dma_cmd = inb(hwif->dma_command); outb(dma_cmd | 6, hwif->dma_command); /* and free any DMA resources */ ide_destroy_dmatable(drive); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 328e2df66550..7a2a7b2a319a 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -126,6 +126,11 @@ static u8 scc_ide_inb(unsigned long port) return (u8)data; } +static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) +{ + return (u8)in_be32((void *)hwif->dma_status); +} + static void scc_ide_insw(unsigned long port, void *addr, u32 count) { u16 *ptr = (u16 *)addr; @@ -773,6 +778,8 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) ide_set_hwifdata(hwif, ports); + hwif->read_sff_dma_status = scc_read_sff_dma_status; + hwif->tf_load = scc_tf_load; hwif->tf_read = scc_tf_read; diff --git a/include/linux/ide.h b/include/linux/ide.h index f58548becac0..ca0efbb0a8b4 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -489,6 +489,8 @@ typedef struct hwif_s { const struct ide_port_ops *port_ops; const struct ide_dma_ops *dma_ops; + u8 (*read_sff_dma_status)(struct hwif_s *); + void (*tf_load)(ide_drive_t *, struct ide_task_s *); void (*tf_read)(ide_drive_t *, struct ide_task_s *); -- cgit From cab7f8eda40d3e3e16b137c67cdddc2cf893c5d7 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:51 +0200 Subject: ide: remove ->dma_{status,command} fields from ide_hwif_t * Use ->dma_base + offset instead of ->dma_{status,command} and remove no longer needed ->dma_{status,command}. While at it: * Use ATA_DMA_* defines. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-dma.c | 42 +++++++++++++++++++++--------------------- drivers/ide/ide-iops.c | 4 ++-- drivers/ide/pci/cmd64x.c | 12 ++++++------ drivers/ide/pci/hpt366.c | 10 +++++----- drivers/ide/pci/ns87415.c | 14 +++++++------- drivers/ide/pci/pdc202xx_old.c | 2 +- drivers/ide/pci/piix.c | 4 ++-- drivers/ide/pci/scc_pata.c | 38 ++++++++++++++++++-------------------- drivers/ide/pci/siimage.c | 4 ++-- drivers/ide/pci/sl82c105.c | 4 ++-- drivers/ide/pci/tc86c001.c | 13 ++++++++----- include/linux/ide.h | 2 -- 12 files changed, 74 insertions(+), 75 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index f3229642c052..d98a9da2699c 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -377,9 +377,10 @@ void ide_dma_host_set(ide_drive_t *drive, int on) dma_stat &= ~(1 << (5 + unit)); if (hwif->host_flags & IDE_HFLAG_MMIO) - writeb(dma_stat, (void __iomem *)hwif->dma_status); + writeb(dma_stat, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); else - outb(dma_stat, hwif->dma_status); + outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS); } EXPORT_SYMBOL_GPL(ide_dma_host_set); @@ -475,18 +476,19 @@ int ide_dma_setup(ide_drive_t *drive) /* specify r/w */ if (mmio) - writeb(reading, (void __iomem *)hwif->dma_command); + writeb(reading, (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); else - outb(reading, hwif->dma_command); + outb(reading, hwif->dma_base + ATA_DMA_CMD); /* read DMA status for INTR & ERROR flags */ dma_stat = hwif->read_sff_dma_status(hwif); /* clear INTR & ERROR flags */ if (mmio) - writeb(dma_stat | 6, (void __iomem *)hwif->dma_status); + writeb(dma_stat | 6, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); else - outb(dma_stat | 6, hwif->dma_status); + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); drive->waiting_for_dma = 1; return 0; @@ -512,12 +514,13 @@ void ide_dma_start(ide_drive_t *drive) * we do this part before issuing the drive cmd. */ if (hwif->host_flags & IDE_HFLAG_MMIO) { - dma_cmd = readb((void __iomem *)hwif->dma_command); + dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); /* start DMA */ - writeb(dma_cmd | 1, (void __iomem *)hwif->dma_command); + writeb(dma_cmd | 1, + (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); } else { - dma_cmd = inb(hwif->dma_command); - outb(dma_cmd | 1, hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd | 1, hwif->dma_base + ATA_DMA_CMD); } hwif->dma = 1; @@ -537,12 +540,13 @@ int __ide_dma_end (ide_drive_t *drive) if (mmio) { /* get DMA command mode */ - dma_cmd = readb((void __iomem *)hwif->dma_command); + dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); /* stop DMA */ - writeb(dma_cmd & ~1, (void __iomem *)hwif->dma_command); + writeb(dma_cmd & ~1, + (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); } else { - dma_cmd = inb(hwif->dma_command); - outb(dma_cmd & ~1, hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); } /* get DMA status */ @@ -550,9 +554,10 @@ int __ide_dma_end (ide_drive_t *drive) if (mmio) /* clear the INTR & ERROR bits */ - writeb(dma_stat | 6, (void __iomem *)hwif->dma_status); + writeb(dma_stat | 6, + (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); else - outb(dma_stat | 6, hwif->dma_status); + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); /* purge DMA mappings */ ide_destroy_dmatable(drive); @@ -888,11 +893,6 @@ void ide_setup_dma(ide_hwif_t *hwif, unsigned long base) { hwif->dma_base = base; - if (!hwif->dma_command) - hwif->dma_command = hwif->dma_base + 0; - if (!hwif->dma_status) - hwif->dma_status = hwif->dma_base + 2; - hwif->dma_ops = &sff_dma_ops; } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index a09bf4369ed8..17cad6c39ee3 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -106,9 +106,9 @@ void SELECT_MASK(ide_drive_t *drive, int mask) static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) - return readb((void __iomem *)hwif->dma_status); + return readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); else - return inb(hwif->dma_status); + return inb(hwif->dma_base + ATA_DMA_STATUS); } static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index cfa784bacf48..ce58bfcdb3c6 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -262,7 +262,7 @@ static int cmd648_dma_test_irq(ide_drive_t *drive) unsigned long base = hwif->dma_base - (hwif->channel * 8); u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); u8 mrdmode = inb(base + 1); #ifdef DEBUG @@ -286,7 +286,7 @@ static int cmd64x_dma_test_irq(ide_drive_t *drive) int irq_reg = hwif->channel ? ARTTIM23 : CFR; u8 irq_mask = hwif->channel ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); u8 irq_stat = 0; (void) pci_read_config_byte(dev, irq_reg, &irq_stat); @@ -317,13 +317,13 @@ static int cmd646_1_dma_end(ide_drive_t *drive) drive->waiting_for_dma = 0; /* get DMA status */ - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* read DMA command state */ - dma_cmd = inb(hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ - outb(dma_cmd & ~1, hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); /* clear the INTR & ERROR bits */ - outb(dma_stat | 6, hwif->dma_status); + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); /* and free any DMA resources */ ide_destroy_dmatable(drive); /* verify good DMA status */ diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 397c6cbe953c..d2f470ec8055 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -801,9 +801,9 @@ static void hpt370_irq_timeout(ide_drive_t *drive) printk(KERN_DEBUG "%s: %d bytes in FIFO\n", drive->name, bfifo & 0x1ff); /* get DMA command mode */ - dma_cmd = inb(hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ - outb(dma_cmd & ~0x1, hwif->dma_command); + outb(dma_cmd & ~0x1, hwif->dma_base + ATA_DMA_CMD); hpt370_clear_engine(drive); } @@ -818,12 +818,12 @@ static void hpt370_dma_start(ide_drive_t *drive) static int hpt370_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); if (dma_stat & 0x01) { /* wait a little */ udelay(20); - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); if (dma_stat & 0x01) hpt370_irq_timeout(drive); } @@ -850,7 +850,7 @@ static int hpt374_dma_test_irq(ide_drive_t *drive) return 0; } - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* return 1 if INTR asserted */ if (dma_stat & 4) return 1; diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 9ffdbb89df5c..76ce112fd857 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -65,7 +65,7 @@ static u8 superio_ide_inb (unsigned long port) static u8 superio_read_sff_dma_status(ide_hwif_t *hwif) { - return superio_ide_inb(hwif->dma_status); + return superio_ide_inb(hwif->dma_base + ATA_DMA_STATUS); } static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) @@ -208,13 +208,13 @@ static int ns87415_dma_end(ide_drive_t *drive) drive->waiting_for_dma = 0; dma_stat = hwif->read_sff_dma_status(hwif); - /* get dma command mode */ - dma_cmd = inb(hwif->dma_command); + /* get DMA command mode */ + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ - outb(dma_cmd & ~1, hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); /* from ERRATA: clear the INTR & ERROR bits */ - dma_cmd = inb(hwif->dma_command); - outb(dma_cmd | 6, hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd | 6, hwif->dma_base + ATA_DMA_CMD); /* and free any DMA resources */ ide_destroy_dmatable(drive); /* verify good DMA status */ @@ -298,7 +298,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) if (!hwif->dma_base) return; - outb(0x60, hwif->dma_status); + outb(0x60, hwif->dma_base + ATA_DMA_STATUS); } static const struct ide_port_ops ns87415_port_ops = { diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index fca89eda5c02..1c2f9df31129 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -206,7 +206,7 @@ static int pdc202xx_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); unsigned long high_16 = hwif->extra_base - 16; - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); u8 sc1d = inb(high_16 + 0x001d); if (hwif->channel) { diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index f04738d14a6f..0ce41b4dddaf 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -227,9 +227,9 @@ static void piix_dma_clear_irq(ide_drive_t *drive) u8 dma_stat; /* clear the INTR & ERROR bits */ - dma_stat = inb(hwif->dma_status); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* Should we force the bit as well ? */ - outb(dma_stat, hwif->dma_status); + outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS); } struct ich_laptop { diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 7a2a7b2a319a..fc163a7772a7 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -128,7 +128,7 @@ static u8 scc_ide_inb(unsigned long port) static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) { - return (u8)in_be32((void *)hwif->dma_status); + return (u8)in_be32((void *)(hwif->dma_base + 4)); } static void scc_ide_insw(unsigned long port, void *addr, u32 count) @@ -266,14 +266,14 @@ static void scc_dma_host_set(ide_drive_t *drive, int on) { ide_hwif_t *hwif = drive->hwif; u8 unit = (drive->select.b.unit & 0x01); - u8 dma_stat = scc_ide_inb(hwif->dma_status); + u8 dma_stat = scc_ide_inb(hwif->dma_base + 4); if (on) dma_stat |= (1 << (5 + unit)); else dma_stat &= ~(1 << (5 + unit)); - scc_ide_outb(dma_stat, hwif->dma_status); + scc_ide_outb(dma_stat, hwif->dma_base + 4); } /** @@ -309,13 +309,13 @@ static int scc_dma_setup(ide_drive_t *drive) out_be32((void __iomem *)(hwif->dma_base + 8), hwif->dmatable_dma); /* specify r/w */ - out_be32((void __iomem *)hwif->dma_command, reading); + out_be32((void __iomem *)hwif->dma_base, reading); - /* read dma_status for INTR & ERROR flags */ - dma_stat = in_be32((void __iomem *)hwif->dma_status); + /* read DMA status for INTR & ERROR flags */ + dma_stat = in_be32((void __iomem *)(hwif->dma_base + 4)); /* clear INTR & ERROR flags */ - out_be32((void __iomem *)hwif->dma_status, dma_stat|6); + out_be32((void __iomem *)(hwif->dma_base + 4), dma_stat | 6); drive->waiting_for_dma = 1; return 0; } @@ -323,10 +323,10 @@ static int scc_dma_setup(ide_drive_t *drive) static void scc_dma_start(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - u8 dma_cmd = scc_ide_inb(hwif->dma_command); + u8 dma_cmd = scc_ide_inb(hwif->dma_base); /* start DMA */ - scc_ide_outb(dma_cmd | 1, hwif->dma_command); + scc_ide_outb(dma_cmd | 1, hwif->dma_base); hwif->dma = 1; wmb(); } @@ -338,13 +338,13 @@ static int __scc_dma_end(ide_drive_t *drive) drive->waiting_for_dma = 0; /* get DMA command mode */ - dma_cmd = scc_ide_inb(hwif->dma_command); + dma_cmd = scc_ide_inb(hwif->dma_base); /* stop DMA */ - scc_ide_outb(dma_cmd & ~1, hwif->dma_command); + scc_ide_outb(dma_cmd & ~1, hwif->dma_base); /* get DMA status */ - dma_stat = scc_ide_inb(hwif->dma_status); + dma_stat = scc_ide_inb(hwif->dma_base + 4); /* clear the INTR & ERROR bits */ - scc_ide_outb(dma_stat | 6, hwif->dma_status); + scc_ide_outb(dma_stat | 6, hwif->dma_base + 4); /* purge DMA mappings */ ide_destroy_dmatable(drive); /* verify good DMA status */ @@ -364,6 +364,7 @@ static int __scc_dma_end(ide_drive_t *drive) static int scc_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); + void __iomem *dma_base = (void __iomem *)hwif->dma_base; unsigned long intsts_port = hwif->dma_base + 0x014; u32 reg; int dma_stat, data_loss = 0; @@ -402,7 +403,7 @@ static int scc_dma_end(ide_drive_t *drive) printk(KERN_WARNING "%s: SERROR\n", SCC_PATA_NAME); out_be32((void __iomem *)intsts_port, INTSTS_SERROR|INTSTS_BMSINT); - out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); continue; } @@ -417,7 +418,7 @@ static int scc_dma_end(ide_drive_t *drive) out_be32((void __iomem *)intsts_port, INTSTS_PRERR|INTSTS_BMSINT); - out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); continue; } @@ -425,12 +426,12 @@ static int scc_dma_end(ide_drive_t *drive) printk(KERN_WARNING "%s: Response Error\n", SCC_PATA_NAME); out_be32((void __iomem *)intsts_port, INTSTS_RERR|INTSTS_BMSINT); - out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); continue; } if (reg & INTSTS_ICERR) { - out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + out_be32(dma_base, in_be32(dma_base) & ~QCHCD_IOS_SS); printk(KERN_WARNING "%s: Illegal Configuration\n", SCC_PATA_NAME); out_be32((void __iomem *)intsts_port, INTSTS_ICERR|INTSTS_BMSINT); @@ -832,9 +833,6 @@ static void __devinit init_hwif_scc(ide_hwif_t *hwif) ports->hwif = hwif; - hwif->dma_command = hwif->dma_base; - hwif->dma_status = hwif->dma_base + 0x04; - /* PTERADD */ out_be32((void __iomem *)(hwif->dma_base + 0x018), hwif->dmatable_dma); diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 6e9d7655d89c..21d7137f7d6c 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -334,7 +334,7 @@ static int siimage_io_dma_test_irq(ide_drive_t *drive) unsigned long addr = siimage_selreg(hwif, 1); /* return 1 if INTR asserted */ - if (hwif->INB(hwif->dma_status) & 4) + if (inb(hwif->dma_base + ATA_DMA_STATUS) & 4) return 1; /* return 1 if Device INTR asserted */ @@ -382,7 +382,7 @@ static int siimage_mmio_dma_test_irq(ide_drive_t *drive) } /* return 1 if INTR asserted */ - if (readb((void __iomem *)hwif->dma_status) & 0x04) + if (readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)) & 4) return 1; /* return 1 if Device INTR asserted */ diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index 6efbde297174..f82a6502c1b7 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -157,9 +157,9 @@ static void sl82c105_dma_lost_irq(ide_drive_t *drive) * Was DMA enabled? If so, disable it - we're resetting the * host. The IDE layer will be handling the drive for us. */ - dma_cmd = inb(hwif->dma_command); + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); if (dma_cmd & 1) { - outb(dma_cmd & ~1, hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); printk("sl82c105: DMA was enabled\n"); } diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c index 9b4b27a4c711..a81d47c55ce1 100644 --- a/drivers/ide/pci/tc86c001.c +++ b/drivers/ide/pci/tc86c001.c @@ -63,7 +63,7 @@ static int tc86c001_timer_expiry(ide_drive_t *drive) ide_hwif_t *hwif = HWIF(drive); ide_expiry_t *expiry = ide_get_hwifdata(hwif); ide_hwgroup_t *hwgroup = HWGROUP(drive); - u8 dma_stat = inb(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); /* Restore a higher level driver's expiry handler first. */ hwgroup->expiry = expiry; @@ -71,21 +71,24 @@ static int tc86c001_timer_expiry(ide_drive_t *drive) if ((dma_stat & 5) == 1) { /* DMA active and no interrupt */ unsigned long sc_base = hwif->config_data; unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04); - u8 dma_cmd = inb(hwif->dma_command); + u8 dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); printk(KERN_WARNING "%s: DMA interrupt possibly stuck, " "attempting recovery...\n", drive->name); /* Stop DMA */ - outb(dma_cmd & ~0x01, hwif->dma_command); + outb(dma_cmd & ~0x01, hwif->dma_base + ATA_DMA_CMD); /* Setup the dummy DMA transfer */ outw(0, sc_base + 0x0a); /* Sector Count */ outw(0, twcr_port); /* Transfer Word Count 1 or 2 */ /* Start the dummy DMA transfer */ - outb(0x00, hwif->dma_command); /* clear R_OR_WCTR for write */ - outb(0x01, hwif->dma_command); /* set START_STOPBM */ + + /* clear R_OR_WCTR for write */ + outb(0x00, hwif->dma_base + ATA_DMA_CMD); + /* set START_STOPBM */ + outb(0x01, hwif->dma_base + ATA_DMA_CMD); /* * If an interrupt was pending, it should come thru shortly. diff --git a/include/linux/ide.h b/include/linux/ide.h index ca0efbb0a8b4..85a32f472ef0 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -526,8 +526,6 @@ typedef struct hwif_s { int irq; /* our irq number */ unsigned long dma_base; /* base addr for dma ports */ - unsigned long dma_command; /* dma command register */ - unsigned long dma_status; /* dma status register */ unsigned long config_data; /* for use by chipset-specific code */ unsigned long select_data; /* for use by chipset-specific code */ -- cgit From 81e8d5a34f7d2a2acbe309cfa5810a9699a63239 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:51 +0200 Subject: ide: remove ide_setup_dma() Export sff_dma_ops and then remove ide_setup_dma(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/palm_bk3710.c | 7 +++---- drivers/ide/ide-dma.c | 12 ++---------- drivers/ide/pci/alim15x3.c | 4 +++- drivers/ide/pci/hpt366.c | 4 +++- drivers/ide/setup-pci.c | 4 +++- include/linux/ide.h | 2 +- 6 files changed, 15 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 023c10753f15..0229d794d909 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -316,15 +316,14 @@ static u8 __devinit palm_bk3710_cable_detect(ide_hwif_t *hwif) static int __devinit palm_bk3710_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d) { - unsigned long base = - hwif->io_ports.data_addr - IDE_PALM_ATA_PRI_REG_OFFSET; - printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name); if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_base = hwif->io_ports.data_addr - IDE_PALM_ATA_PRI_REG_OFFSET; + + hwif->dma_ops = &sff_dma_ops; return 0; } diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index d98a9da2699c..ac342ebf6c54 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -878,7 +878,7 @@ int ide_allocate_dma_engine(ide_hwif_t *hwif) } EXPORT_SYMBOL_GPL(ide_allocate_dma_engine); -static const struct ide_dma_ops sff_dma_ops = { +const struct ide_dma_ops sff_dma_ops = { .dma_host_set = ide_dma_host_set, .dma_setup = ide_dma_setup, .dma_exec_cmd = ide_dma_exec_cmd, @@ -888,13 +888,5 @@ static const struct ide_dma_ops sff_dma_ops = { .dma_timeout = ide_dma_timeout, .dma_lost_irq = ide_dma_lost_irq, }; - -void ide_setup_dma(ide_hwif_t *hwif, unsigned long base) -{ - hwif->dma_base = base; - - hwif->dma_ops = &sff_dma_ops; -} - -EXPORT_SYMBOL_GPL(ide_setup_dma); +EXPORT_SYMBOL_GPL(sff_dma_ops); #endif /* CONFIG_BLK_DEV_IDEDMA_SFF */ diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 80d19c0eb780..8015f6f65488 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -483,7 +483,9 @@ static int __devinit init_dma_ali15x3(ide_hwif_t *hwif, if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_base = base; + + hwif->dma_ops = &sff_dma_ops; return 0; } diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index d2f470ec8055..201e5ddae921 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -1346,7 +1346,9 @@ static int __devinit init_dma_hpt366(ide_hwif_t *hwif, if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_base = base; + + hwif->dma_ops = &sff_dma_ops; return 0; } diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index acb467c6f345..b047013f3652 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -377,7 +377,9 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) if (ide_allocate_dma_engine(hwif)) return -1; - ide_setup_dma(hwif, base); + hwif->dma_base = base; + + hwif->dma_ops = &sff_dma_ops; } return 0; diff --git a/include/linux/ide.h b/include/linux/ide.h index 85a32f472ef0..6d774607e32a 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1007,6 +1007,7 @@ void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *); #ifdef CONFIG_BLK_DEV_IDEDMA_PCI int ide_pci_set_master(struct pci_dev *, const char *); unsigned long ide_pci_dma_base(ide_hwif_t *, const struct ide_port_info *); +extern const struct ide_dma_ops sff_dma_ops; int ide_hwif_setup_dma(ide_hwif_t *, const struct ide_port_info *); #else static inline int ide_hwif_setup_dma(ide_hwif_t *hwif, @@ -1164,7 +1165,6 @@ void ide_destroy_dmatable(ide_drive_t *); extern int ide_build_dmatable(ide_drive_t *, struct request *); int ide_allocate_dma_engine(ide_hwif_t *); void ide_release_dma_engine(ide_hwif_t *); -void ide_setup_dma(ide_hwif_t *, unsigned long); void ide_dma_host_set(ide_drive_t *, int); extern int ide_dma_setup(ide_drive_t *); -- cgit From ebb00fb55d0566bb3e81518122a57b4b3bedf1e4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:51 +0200 Subject: ide: factor out simplex handling from ide_pci_dma_base() * Factor out simplex handling from ide_pci_dma_base() to ide_pci_check_simplex(). * Set hwif->dma_base early in ->init_dma method / ide_hwif_setup_dma() and reset it in ide_init_port() if DMA initialization fails. * Use ->read_sff_dma_status instead of ->INB in ide_pci_dma_base(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 1 + drivers/ide/pci/alim15x3.c | 12 +++++++++--- drivers/ide/pci/hpt366.c | 12 +++++++++--- drivers/ide/setup-pci.c | 35 +++++++++++++++++++++++------------ include/linux/ide.h | 1 + 5 files changed, 43 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 9d8686a49fcf..ec5f58c6f19a 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1363,6 +1363,7 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port, if (rc < 0) { printk(KERN_INFO "%s: DMA disabled\n", hwif->name); + hwif->dma_base = 0; hwif->swdma_mask = 0; hwif->mwdma_mask = 0; hwif->ultra_mask = 0; diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 8015f6f65488..5ef7817ac64f 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -471,7 +471,15 @@ static int __devinit init_dma_ali15x3(ide_hwif_t *hwif, struct pci_dev *dev = to_pci_dev(hwif->dev); unsigned long base = ide_pci_dma_base(hwif, d); - if (base == 0 || ide_pci_set_master(dev, d->name) < 0) + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) return -1; if (!hwif->channel) @@ -483,8 +491,6 @@ static int __devinit init_dma_ali15x3(ide_hwif_t *hwif, if (ide_allocate_dma_engine(hwif)) return -1; - hwif->dma_base = base; - hwif->dma_ops = &sff_dma_ops; return 0; diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 201e5ddae921..e5651cefa395 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -1320,7 +1320,15 @@ static int __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long flags, base = ide_pci_dma_base(hwif, d); u8 dma_old, dma_new, masterdma = 0, slavedma = 0; - if (base == 0 || ide_pci_set_master(dev, d->name) < 0) + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) return -1; dma_old = inb(base + 2); @@ -1346,8 +1354,6 @@ static int __devinit init_dma_hpt366(ide_hwif_t *hwif, if (ide_allocate_dma_engine(hwif)) return -1; - hwif->dma_base = base; - hwif->dma_ops = &sff_dma_ops; return 0; diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index b047013f3652..c1b609d9cb28 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -73,15 +73,12 @@ static void ide_pci_clear_simplex(unsigned long dma_base, const char *name) * @d: IDE port info * * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space. - * Where a device has a partner that is already in DMA mode we check - * and enforce IDE simplex rules. */ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) { struct pci_dev *dev = to_pci_dev(hwif->dev); unsigned long dma_base = 0; - u8 dma_stat = 0; if (hwif->host_flags & IDE_HFLAG_MMIO) return hwif->dma_base; @@ -102,11 +99,19 @@ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) if (hwif->channel) dma_base += 8; - if (d->host_flags & IDE_HFLAG_CS5520) + return dma_base; +} +EXPORT_SYMBOL_GPL(ide_pci_dma_base); + +int ide_pci_check_simplex(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + u8 dma_stat; + + if (d->host_flags & (IDE_HFLAG_MMIO | IDE_HFLAG_CS5520)) goto out; if (d->host_flags & IDE_HFLAG_CLEAR_SIMPLEX) { - ide_pci_clear_simplex(dma_base, d->name); + ide_pci_clear_simplex(hwif->dma_base, d->name); goto out; } @@ -120,15 +125,15 @@ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) * we tune the drive then try to grab DMA ownership if we want to be * the DMA end. This has to be become dynamic to handle hot-plug. */ - dma_stat = hwif->INB(dma_base + 2); + dma_stat = hwif->read_sff_dma_status(hwif); if ((dma_stat & 0x80) && hwif->mate && hwif->mate->dma_base) { printk(KERN_INFO "%s: simplex device: DMA disabled\n", d->name); - dma_base = 0; + return -1; } out: - return dma_base; + return 0; } -EXPORT_SYMBOL_GPL(ide_pci_dma_base); +EXPORT_SYMBOL_GPL(ide_pci_check_simplex); /* * Set up BM-DMA capability (PnP BIOS should have done this) @@ -363,7 +368,15 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) (dev->class & 0x80))) { unsigned long base = ide_pci_dma_base(hwif, d); - if (base == 0 || ide_pci_set_master(dev, d->name) < 0) + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) return -1; if (hwif->host_flags & IDE_HFLAG_MMIO) @@ -377,8 +390,6 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) if (ide_allocate_dma_engine(hwif)) return -1; - hwif->dma_base = base; - hwif->dma_ops = &sff_dma_ops; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 6d774607e32a..a179a7f6e444 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1008,6 +1008,7 @@ void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *); int ide_pci_set_master(struct pci_dev *, const char *); unsigned long ide_pci_dma_base(ide_hwif_t *, const struct ide_port_info *); extern const struct ide_dma_ops sff_dma_ops; +int ide_pci_check_simplex(ide_hwif_t *, const struct ide_port_info *); int ide_hwif_setup_dma(ide_hwif_t *, const struct ide_port_info *); #else static inline int ide_hwif_setup_dma(ide_hwif_t *hwif, -- cgit From c6dfa867bb45f4bff2e48f3bc89ab1d6a7ab4c21 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:51 +0200 Subject: ide: add ->exec_command method Add ->exec_command method for writing ATA Command register and use it instead of ->OUTBSYNC. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 3 +-- drivers/ide/ide-iops.c | 19 ++++++++++++++----- drivers/ide/ide-probe.c | 6 +++--- drivers/ide/ide-taskfile.c | 2 +- drivers/ide/pci/scc_pata.c | 9 +++++++++ drivers/ide/ppc/pmac.c | 9 +++++++++ drivers/scsi/ide-scsi.c | 3 +-- include/linux/ide.h | 3 ++- 8 files changed, 40 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 661b75a89d4d..ca0c04a919cb 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -437,8 +437,7 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->OUTBSYNC(hwif, WIN_IDLEIMMEDIATE, - hwif->io_ports.command_addr); + hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); if (rq->errors >= ERROR_MAX) { ide_kill_rq(drive, rq); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 17cad6c39ee3..d8db25581909 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -103,6 +103,14 @@ void SELECT_MASK(ide_drive_t *drive, int mask) port_ops->maskproc(drive, mask); } +static void ide_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); + else + outb(cmd, hwif->io_ports.command_addr); +} + static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) @@ -331,6 +339,7 @@ static void ata_output_data(ide_drive_t *drive, struct request *rq, void default_hwif_transport(ide_hwif_t *hwif) { + hwif->exec_command = ide_exec_command; hwif->read_sff_dma_status = ide_read_sff_dma_status; hwif->tf_load = ide_tf_load; @@ -696,7 +705,7 @@ int ide_driveid_update(ide_drive_t *drive) SELECT_MASK(drive, 1); ide_set_irq(drive, 0); msleep(50); - hwif->OUTBSYNC(hwif, WIN_IDENTIFY, hwif->io_ports.command_addr); + hwif->exec_command(hwif, WIN_IDENTIFY); timeout = jiffies + WAIT_WORSTCASE; do { if (time_after(jiffies, timeout)) { @@ -783,7 +792,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) ide_set_irq(drive, 0); hwif->OUTB(speed, io_ports->nsect_addr); hwif->OUTB(SETFEATURES_XFER, io_ports->feature_addr); - hwif->OUTBSYNC(hwif, WIN_SETFEATURES, io_ports->command_addr); + hwif->exec_command(hwif, WIN_SETFEATURES); if (drive->quirk_list == 2) ide_set_irq(drive, 1); @@ -891,7 +900,7 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler, spin_lock_irqsave(&ide_lock, flags); __ide_set_handler(drive, handler, timeout, expiry); - hwif->OUTBSYNC(hwif, cmd, hwif->io_ports.command_addr); + hwif->exec_command(hwif, cmd); /* * Drive takes 400nS to respond, we must avoid the IRQ being * serviced before that. @@ -909,7 +918,7 @@ void ide_execute_pkt_cmd(ide_drive_t *drive) unsigned long flags; spin_lock_irqsave(&ide_lock, flags); - hwif->OUTBSYNC(hwif, WIN_PACKETCMD, hwif->io_ports.command_addr); + hwif->exec_command(hwif, WIN_PACKETCMD); ndelay(400); spin_unlock_irqrestore(&ide_lock, flags); } @@ -1116,7 +1125,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) pre_reset(drive); SELECT_DRIVE(drive); udelay (20); - hwif->OUTBSYNC(hwif, WIN_SRST, io_ports->command_addr); + hwif->exec_command(hwif, WIN_SRST); ndelay(400); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; hwgroup->polling = 1; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index ec5f58c6f19a..d1e834a1c5cd 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -295,7 +295,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) hwif->OUTB(0, io_ports->feature_addr); /* ask drive for ID */ - hwif->OUTBSYNC(hwif, cmd, hwif->io_ports.command_addr); + hwif->exec_command(hwif, cmd); timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; timeout += jiffies; @@ -482,7 +482,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) msleep(50); SELECT_DRIVE(drive); msleep(50); - hwif->OUTBSYNC(hwif, WIN_SRST, io_ports->command_addr); + hwif->exec_command(hwif, WIN_SRST); (void)ide_busy_sleep(hwif); rc = try_to_identify(drive, cmd); } @@ -518,7 +518,7 @@ static void enable_nest (ide_drive_t *drive) printk("%s: enabling %s -- ", hwif->name, drive->id->model); SELECT_DRIVE(drive); msleep(50); - hwif->OUTBSYNC(hwif, EXABYTE_ENABLE_NEST, hwif->io_ports.command_addr); + hwif->exec_command(hwif, EXABYTE_ENABLE_NEST); if (ide_busy_sleep(hwif)) { printk(KERN_CONT "failed (timeout)\n"); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 1fbdb746dc88..c56be289417e 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -88,7 +88,7 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) switch (task->data_phase) { case TASKFILE_MULTI_OUT: case TASKFILE_OUT: - hwif->OUTBSYNC(hwif, tf->command, hwif->io_ports.command_addr); + hwif->exec_command(hwif, tf->command); ndelay(400); /* FIXME */ return pre_task_out_intr(drive, task->rq); case TASKFILE_MULTI_IN: diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index fc163a7772a7..d140dfd565a4 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -126,6 +126,14 @@ static u8 scc_ide_inb(unsigned long port) return (u8)data; } +static void scc_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + out_be32((void *)hwif->io_ports.command_addr, cmd); + eieio(); + in_be32((void *)(hwif->dma_base + 0x01c)); + eieio(); +} + static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) { return (u8)in_be32((void *)(hwif->dma_base + 4)); @@ -779,6 +787,7 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) ide_set_hwifdata(hwif, ports); + hwif->exec_command = scc_exec_command; hwif->read_sff_dma_status = scc_read_sff_dma_status; hwif->tf_load = scc_tf_load; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index e68e33bb2c35..7c3a84f8fbed 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -495,6 +495,13 @@ static void pmac_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) + IDE_TIMING_CONFIG)); } +static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); + (void)readl((void __iomem *)(hwif->io_ports.data_addr + + IDE_TIMING_CONFIG)); +} + /* * Old tuning functions (called on hdparm -p), sets up drive PIO timings */ @@ -1092,6 +1099,8 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) if (hwif == NULL) return -ENOENT; + hwif->exec_command = pmac_exec_command; + /* Setup MMIO ops */ default_hwif_mmiops(hwif); hwif->OUTBSYNC = pmac_outbsync; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index f843c1383a4b..80123890ced0 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -248,8 +248,7 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->OUTBSYNC(hwif, WIN_IDLEIMMEDIATE, - hwif->io_ports.command_addr); + hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); rq->errors++; diff --git a/include/linux/ide.h b/include/linux/ide.h index a179a7f6e444..d77c1994d0e3 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -489,7 +489,8 @@ typedef struct hwif_s { const struct ide_port_ops *port_ops; const struct ide_dma_ops *dma_ops; - u8 (*read_sff_dma_status)(struct hwif_s *); + void (*exec_command)(struct hwif_s *, u8); + u8 (*read_sff_dma_status)(struct hwif_s *); void (*tf_load)(ide_drive_t *, struct ide_task_s *); void (*tf_read)(ide_drive_t *, struct ide_task_s *); -- cgit From b73c7ee25da6133f97f47ffd3557288417da7c76 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:52 +0200 Subject: ide: add ->read_status method * Remove ide_read_status() inline helper. * Add ->read_status method for reading ATA Status register and use it instead of ->INB. While at it: * Don't use HWGROUP() macro. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 4 +++- drivers/ide/ide-atapi.c | 2 +- drivers/ide/ide-cd.c | 12 ++++++++---- drivers/ide/ide-dma.c | 5 +++-- drivers/ide/ide-floppy.c | 3 ++- drivers/ide/ide-io.c | 19 ++++++++++--------- drivers/ide/ide-iops.c | 33 ++++++++++++++++++++++----------- drivers/ide/ide-probe.c | 22 +++++++++++----------- drivers/ide/ide-tape.c | 6 ++++-- drivers/ide/ide-taskfile.c | 23 ++++++++++++++--------- drivers/ide/pci/ns87415.c | 8 +++++++- drivers/ide/pci/scc_pata.c | 6 ++++++ drivers/ide/pci/sgiioc4.c | 19 ++++++++++--------- drivers/scsi/ide-scsi.c | 2 +- include/linux/ide.h | 8 +------- 15 files changed, 103 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 850fe9342a1f..f73422dd8474 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -375,12 +375,14 @@ static int icside_dma_test_irq(ide_drive_t *drive) static void icside_dma_timeout(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; + printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); if (icside_dma_test_irq(drive)) return; - ide_dump_status(drive, "DMA timeout", ide_read_status(drive)); + ide_dump_status(drive, "DMA timeout", hwif->read_status(hwif)); icside_dma_end(drive); } diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 2802031de670..6554c4225a05 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -35,7 +35,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, } /* Clear the interrupt */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { if (hwif->dma_ops->dma_end(drive) || diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 6e29dd532090..82879a1a89e5 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -280,11 +280,12 @@ static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 st) */ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) { - struct request *rq = HWGROUP(drive)->rq; + ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->hwgroup->rq; int stat, err, sense_key; /* check for errors */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat_ret) *stat_ret = stat; @@ -606,6 +607,8 @@ static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, int len, int ireason, int rw) { + ide_hwif_t *hwif = drive->hwif; + /* * ireason == 0: the drive wants to receive data from us * ireason == 2: the drive is expecting to transfer data to us @@ -624,7 +627,7 @@ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, * Some drives (ASUS) seem to tell us that status info is * available. Just get it and ignore. */ - (void)ide_read_status(drive); + (void)hwif->read_status(hwif); return 0; } else { /* drive wants a command packet, or invalid ireason... */ @@ -1199,8 +1202,9 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, if (blk_fs_request(rq)) { if (info->cd_flags & IDE_CD_FLAG_SEEKING) { + ide_hwif_t *hwif = drive->hwif; unsigned long elapsed = jiffies - info->start_seek; - int stat = ide_read_status(drive); + int stat = hwif->read_status(hwif); if ((stat & SEEK_STAT) != SEEK_STAT) { if (elapsed < IDECD_SEEK_TIMEOUT) { diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index ac342ebf6c54..ecf60dbbe3e9 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -100,10 +100,11 @@ static const struct drive_list_entry drive_blacklist [] = { ide_startstop_t ide_dma_intr (ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; u8 stat = 0, dma_stat = 0; - dma_stat = drive->hwif->dma_ops->dma_end(drive); - stat = ide_read_status(drive); + dma_stat = hwif->dma_ops->dma_end(drive); + stat = hwif->read_status(hwif); if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { if (!dma_stat) { diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 011d72011cc4..6f5294cfff23 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -949,11 +949,12 @@ static int idefloppy_get_format_progress(ide_drive_t *drive, int __user *arg) /* Else assume format_unit has finished, and we're at 0x10000 */ } else { + ide_hwif_t *hwif = drive->hwif; unsigned long flags; u8 stat; local_irq_save(flags); - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); local_irq_restore(flags); progress_indication = ((stat & SEEK_STAT) == 0) ? 0 : 0x10000; diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index ca0c04a919cb..fdc221ce9920 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -381,8 +381,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 if (err == ABRT_ERR) { if (drive->select.b.lba && /* some newer drives don't support WIN_SPECIFY */ - hwif->INB(hwif->io_ports.command_addr) == - WIN_SPECIFY) + hwif->read_status(hwif) == WIN_SPECIFY) return ide_stopped; } else if ((err & BAD_CRC) == BAD_CRC) { /* UDMA crc error, just retry the operation */ @@ -408,7 +407,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 return ide_stopped; } - if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) + if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) rq->errors |= ERROR_RESET; if ((rq->errors & ERROR_RESET) == ERROR_RESET) { @@ -435,7 +434,7 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u /* add decoding error stuff */ } - if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) + if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); @@ -711,7 +710,8 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, #ifdef DEBUG printk("%s: DRIVE_CMD (null)\n", drive->name); #endif - ide_end_drive_cmd(drive, ide_read_status(drive), ide_read_error(drive)); + ide_end_drive_cmd(drive, hwif->read_status(hwif), + ide_read_error(drive)); return ide_stopped; } @@ -1141,7 +1141,7 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); (void)hwif->dma_ops->dma_end(drive); ret = ide_error(drive, "dma timeout error", - ide_read_status(drive)); + hwif->read_status(hwif)); } else { printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name); hwif->dma_ops->dma_timeout(drive); @@ -1266,7 +1266,7 @@ void ide_timer_expiry (unsigned long data) } else startstop = ide_error(drive, "irq timeout", - ide_read_status(drive)); + hwif->read_status(hwif)); } drive->service_time = jiffies - drive->service_start; spin_lock_irq(&ide_lock); @@ -1322,7 +1322,8 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) */ do { if (hwif->irq == irq) { - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->read_status(hwif); + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { /* Try to not flood the console with msgs */ static unsigned long last_msgtime, count; @@ -1412,7 +1413,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) * Whack the status register, just in case * we have a leftover pending IRQ. */ - (void) hwif->INB(hwif->io_ports.status_addr); + (void)hwif->read_status(hwif); #endif /* CONFIG_BLK_DEV_IDEPCI */ } spin_unlock_irqrestore(&ide_lock, flags); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index d8db25581909..086eceaeeafd 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -111,6 +111,14 @@ static void ide_exec_command(ide_hwif_t *hwif, u8 cmd) outb(cmd, hwif->io_ports.command_addr); } +static u8 ide_read_status(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->io_ports.status_addr); + else + return inb(hwif->io_ports.status_addr); +} + static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) @@ -340,6 +348,7 @@ static void ata_output_data(ide_drive_t *drive, struct request *rq, void default_hwif_transport(ide_hwif_t *hwif) { hwif->exec_command = ide_exec_command; + hwif->read_status = ide_read_status; hwif->read_sff_dma_status = ide_read_sff_dma_status; hwif->tf_load = ide_tf_load; @@ -505,7 +514,7 @@ int drive_is_ready (ide_drive_t *drive) stat = ide_read_altstatus(drive); else /* Note: this may clear a pending IRQ!! */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat & BUSY_STAT) /* drive busy: definitely not interrupting */ @@ -530,24 +539,25 @@ EXPORT_SYMBOL(drive_is_ready); */ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long timeout, u8 *rstat) { + ide_hwif_t *hwif = drive->hwif; unsigned long flags; int i; u8 stat; udelay(1); /* spec allows drive 400ns to assert "BUSY" */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat & BUSY_STAT) { local_irq_set(flags); timeout += jiffies; - while ((stat = ide_read_status(drive)) & BUSY_STAT) { + while ((stat = hwif->read_status(hwif)) & BUSY_STAT) { if (time_after(jiffies, timeout)) { /* * One last read after the timeout in case * heavy interrupt load made us not make any * progress during the timeout.. */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (!(stat & BUSY_STAT)) break; @@ -567,7 +577,7 @@ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long ti */ for (i = 0; i < 10; i++) { udelay(1); - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (OK_STAT(stat, good, bad)) { *rstat = stat; @@ -718,7 +728,7 @@ int ide_driveid_update(ide_drive_t *drive) } while (stat & BUSY_STAT); msleep(50); /* wait for IRQ and DRQ_STAT */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (!OK_STAT(stat, DRQ_STAT, BAD_R_STAT)) { SELECT_MASK(drive, 0); @@ -733,7 +743,7 @@ int ide_driveid_update(ide_drive_t *drive) return 0; } hwif->input_data(drive, NULL, id, SECTOR_SIZE); - (void)ide_read_status(drive); /* clear drive IRQ */ + (void)hwif->read_status(hwif); /* clear drive IRQ */ local_irq_enable(); local_irq_restore(flags); ide_fix_driveid(id); @@ -943,12 +953,13 @@ static ide_startstop_t do_reset1 (ide_drive_t *, int); */ static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) { - ide_hwgroup_t *hwgroup = HWGROUP(drive); + ide_hwif_t *hwif = drive->hwif; + ide_hwgroup_t *hwgroup = hwif->hwgroup; u8 stat; SELECT_DRIVE(drive); udelay (10); - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (OK_STAT(stat, 0, BUSY_STAT)) printk("%s: ATAPI reset complete\n", drive->name); @@ -994,7 +1005,7 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) } } - tmp = ide_read_status(drive); + tmp = hwif->read_status(hwif); if (!OK_STAT(tmp, 0, BUSY_STAT)) { if (time_before(jiffies, hwgroup->poll_timeout)) { @@ -1208,7 +1219,7 @@ int ide_wait_not_busy(ide_hwif_t *hwif, unsigned long timeout) * about locking issues (2.5 work ?). */ mdelay(1); - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->read_status(hwif); if ((stat & BUSY_STAT) == 0) return 0; /* diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index d1e834a1c5cd..c42fcfedcbf6 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -276,7 +276,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) if (io_ports->ctl_addr) { a = ide_read_altstatus(drive); - s = ide_read_status(drive); + s = hwif->read_status(hwif); if ((a ^ s) & ~INDEX_STAT) /* ancient Seagate drives, broken interfaces */ printk(KERN_INFO "%s: probing with STATUS(0x%02x) " @@ -307,12 +307,12 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) /* give drive a breather */ msleep(50); s = use_altstatus ? ide_read_altstatus(drive) - : ide_read_status(drive); + : hwif->read_status(hwif); } while (s & BUSY_STAT); /* wait for IRQ and DRQ_STAT */ msleep(50); - s = ide_read_status(drive); + s = hwif->read_status(hwif); if (OK_STAT(s, DRQ_STAT, BAD_R_STAT)) { unsigned long flags; @@ -324,7 +324,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) /* drive responded with ID */ rc = 0; /* clear drive IRQ */ - (void)ide_read_status(drive); + (void)hwif->read_status(hwif); local_irq_restore(flags); } else { /* drive refused ID */ @@ -371,7 +371,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) ide_set_irq(drive, 0); /* clear drive IRQ */ - (void)ide_read_status(drive); + (void)hwif->read_status(hwif); udelay(5); irq = probe_irq_off(cookie); if (!hwif->irq) { @@ -396,7 +396,7 @@ static int ide_busy_sleep(ide_hwif_t *hwif) do { msleep(50); - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->read_status(hwif); if ((stat & BUSY_STAT) == 0) return 0; } while (time_before(jiffies, timeout)); @@ -461,7 +461,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) return 3; } - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (OK_STAT(stat, READY_STAT, BUSY_STAT) || drive->present || cmd == WIN_PIDENTIFY) { @@ -471,7 +471,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) rc = try_to_identify(drive,cmd); } - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat == (BUSY_STAT | READY_STAT)) return 4; @@ -488,7 +488,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) } /* ensure drive IRQ is clear */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (rc == 1) printk(KERN_ERR "%s: no response (status = 0x%02x)\n", @@ -502,7 +502,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) SELECT_DRIVE(&hwif->drives[0]); msleep(50); /* ensure drive irq is clear */ - (void)ide_read_status(drive); + (void)hwif->read_status(hwif); } return rc; } @@ -527,7 +527,7 @@ static void enable_nest (ide_drive_t *drive) msleep(50); - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (!OK_STAT(stat, 0, BAD_STAT)) printk(KERN_CONT "failed (status = 0x%02x)\n", stat); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 353dd11b9283..ef54728a74b0 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -927,11 +927,12 @@ static void idetape_create_mode_sense_cmd(struct ide_atapi_pc *pc, u8 page_code) static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; idetape_tape_t *tape = drive->driver_data; struct ide_atapi_pc *pc = tape->pc; u8 stat; - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat & SEEK_STAT) { if (stat & ERR_STAT) { @@ -980,6 +981,7 @@ static void ide_tape_create_rw_cmd(idetape_tape_t *tape, static ide_startstop_t idetape_do_request(ide_drive_t *drive, struct request *rq, sector_t block) { + ide_hwif_t *hwif = drive->hwif; idetape_tape_t *tape = drive->driver_data; struct ide_atapi_pc *pc = NULL; struct request *postponed_rq = tape->postponed_rq; @@ -1017,7 +1019,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, * If the tape is still busy, postpone our request and service * the other device meanwhile. */ - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (!drive->dsc_overlap && !(rq->cmd[0] & REQ_IDETAPE_PC2)) set_bit(IDETAPE_FLAG_IGNORE_DSC, &tape->flags); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index c56be289417e..fc2b3957afac 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -124,7 +124,8 @@ EXPORT_SYMBOL_GPL(do_rw_taskfile); */ static ide_startstop_t set_multmode_intr(ide_drive_t *drive) { - u8 stat = ide_read_status(drive); + ide_hwif_t *hwif = drive->hwif; + u8 stat = hwif->read_status(hwif); if (OK_STAT(stat, READY_STAT, BAD_STAT)) drive->mult_count = drive->mult_req; @@ -141,10 +142,11 @@ static ide_startstop_t set_multmode_intr(ide_drive_t *drive) */ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; int retries = 5; u8 stat; - while (((stat = ide_read_status(drive)) & BUSY_STAT) && retries--) + while (((stat = hwif->read_status(hwif)) & BUSY_STAT) && retries--) udelay(10); if (OK_STAT(stat, READY_STAT, BAD_STAT)) @@ -162,7 +164,8 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) */ static ide_startstop_t recal_intr(ide_drive_t *drive) { - u8 stat = ide_read_status(drive); + ide_hwif_t *hwif = drive->hwif; + u8 stat = hwif->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "recal_intr", stat); @@ -174,11 +177,12 @@ static ide_startstop_t recal_intr(ide_drive_t *drive) */ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) { - ide_task_t *args = HWGROUP(drive)->rq->special; + ide_hwif_t *hwif = drive->hwif; + ide_task_t *args = hwif->hwgroup->rq->special; u8 stat; local_irq_enable_in_hardirq(); - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "task_no_data_intr", stat); @@ -192,6 +196,7 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) static u8 wait_drive_not_busy(ide_drive_t *drive) { + ide_hwif_t *hwif = drive->hwif; int retries; u8 stat; @@ -200,7 +205,7 @@ static u8 wait_drive_not_busy(ide_drive_t *drive) * take up to 6 ms on some ATAPI devices, so we will wait max 10 ms. */ for (retries = 0; retries < 1000; retries++) { - stat = ide_read_status(drive); + stat = hwif->read_status(hwif); if (stat & BUSY_STAT) udelay(10); @@ -383,8 +388,8 @@ static ide_startstop_t task_in_unexpected(ide_drive_t *drive, struct request *rq static ide_startstop_t task_in_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - struct request *rq = HWGROUP(drive)->rq; - u8 stat = ide_read_status(drive); + struct request *rq = hwif->hwgroup->rq; + u8 stat = hwif->read_status(hwif); /* Error? */ if (stat & ERR_STAT) @@ -418,7 +423,7 @@ static ide_startstop_t task_out_intr (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; struct request *rq = HWGROUP(drive)->rq; - u8 stat = ide_read_status(drive); + u8 stat = hwif->read_status(hwif); if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) return task_error(drive, rq, __func__, stat); diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 76ce112fd857..b20e5f01ac89 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -63,6 +63,11 @@ static u8 superio_ide_inb (unsigned long port) return inb(port); } +static u8 superio_read_status(ide_hwif_t *hwif) +{ + return superio_ide_inb(hwif->io_ports.status_addr); +} + static u8 superio_read_sff_dma_status(ide_hwif_t *hwif) { return superio_ide_inb(hwif->dma_base + ATA_DMA_STATUS); @@ -127,6 +132,7 @@ static void __devinit superio_ide_init_iops (struct hwif_s *hwif) tmp = superio_ide_inb(superio_ide_dma_status[port]); outb(tmp | 0x66, superio_ide_dma_status[port]); + hwif->read_status = superio_read_status; hwif->read_sff_dma_status = superio_read_sff_dma_status; hwif->tf_read = superio_tf_read; @@ -283,7 +289,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) outb(8, hwif->io_ports.ctl_addr); do { udelay(50); - stat = hwif->INB(hwif->io_ports.status_addr); + stat = hwif->read_status(hwif); if (stat == 0xff) break; } while ((stat & BUSY_STAT) && --timeout); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index d140dfd565a4..3d72a5e03f3d 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -134,6 +134,11 @@ static void scc_exec_command(ide_hwif_t *hwif, u8 cmd) eieio(); } +static u8 scc_read_status(ide_hwif_t *hwif) +{ + return (u8)in_be32((void *)hwif->io_ports.status_addr); +} + static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) { return (u8)in_be32((void *)(hwif->dma_base + 4)); @@ -788,6 +793,7 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) ide_set_hwifdata(hwif, ports); hwif->exec_command = scc_exec_command; + hwif->read_status = scc_read_status; hwif->read_sff_dma_status = scc_read_sff_dma_status; hwif->tf_load = scc_tf_load; diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 76afa1f9c599..3005a6334a58 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -127,7 +127,7 @@ sgiioc4_checkirq(ide_hwif_t * hwif) return 0; } -static u8 sgiioc4_INB(unsigned long); +static u8 sgiioc4_read_status(ide_hwif_t *); static int sgiioc4_clearirq(ide_drive_t * drive) @@ -141,18 +141,19 @@ sgiioc4_clearirq(ide_drive_t * drive) intr_reg = readl((void __iomem *)other_ir); if (intr_reg & 0x03) { /* Valid IOC4-IDE interrupt */ /* - * Using sgiioc4_INB to read the Status register has a side - * effect of clearing the interrupt. The first read should + * Using sgiioc4_read_status to read the Status register has a + * side effect of clearing the interrupt. The first read should * clear it if it is set. The second read should return * a "clear" status if it got cleared. If not, then spin * for a bit trying to clear it. */ - u8 stat = sgiioc4_INB(io_ports->status_addr); + u8 stat = sgiioc4_read_status(hwif); int count = 0; - stat = sgiioc4_INB(io_ports->status_addr); + + stat = sgiioc4_read_status(hwif); while ((stat & 0x80) && (count++ < 100)) { udelay(1); - stat = sgiioc4_INB(io_ports->status_addr); + stat = sgiioc4_read_status(hwif); } if (intr_reg & 0x02) { @@ -304,9 +305,9 @@ sgiioc4_dma_lost_irq(ide_drive_t * drive) ide_dma_lost_irq(drive); } -static u8 -sgiioc4_INB(unsigned long port) +static u8 sgiioc4_read_status(ide_hwif_t *hwif) { + unsigned long port = hwif->io_ports.status_addr; u8 reg = (u8) readb((void __iomem *) port); if ((port & 0xFFF) == 0x11C) { /* Status register of IOC4 */ @@ -628,7 +629,7 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) /* Initializing chipset IRQ Registers */ writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); - hwif->INB = &sgiioc4_INB; + hwif->read_status = sgiioc4_read_status; idx[0] = hwif->index; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 80123890ced0..2a86af91f64a 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -246,7 +246,7 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) { ide_hwif_t *hwif = drive->hwif; - if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT)) + if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); diff --git a/include/linux/ide.h b/include/linux/ide.h index d77c1994d0e3..a23ae25d7da8 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -490,6 +490,7 @@ typedef struct hwif_s { const struct ide_dma_ops *dma_ops; void (*exec_command)(struct hwif_s *, u8); + u8 (*read_status)(struct hwif_s *); u8 (*read_sff_dma_status)(struct hwif_s *); void (*tf_load)(ide_drive_t *, struct ide_task_s *); @@ -1362,13 +1363,6 @@ static inline void ide_set_irq(ide_drive_t *drive, int on) hwif->io_ports.ctl_addr); } -static inline u8 ide_read_status(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - - return hwif->INB(hwif->io_ports.status_addr); -} - static inline u8 ide_read_altstatus(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; -- cgit From 1f6d8a0fd8f6cc5ee2219a8cf9b2da16dfd67397 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:52 +0200 Subject: ide: add ->read_altstatus method * Remove ide_read_altstatus() inline helper. * Add ->read_altstatus method for reading ATA Alternate Status register and use it instead of ->INB. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-iops.c | 13 +++++++++++-- drivers/ide/ide-probe.c | 4 ++-- drivers/ide/pci/scc_pata.c | 6 ++++++ include/linux/ide.h | 8 +------- 4 files changed, 20 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 086eceaeeafd..e106954e13f9 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -119,6 +119,14 @@ static u8 ide_read_status(ide_hwif_t *hwif) return inb(hwif->io_ports.status_addr); } +static u8 ide_read_altstatus(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->io_ports.ctl_addr); + else + return inb(hwif->io_ports.ctl_addr); +} + static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) @@ -349,6 +357,7 @@ void default_hwif_transport(ide_hwif_t *hwif) { hwif->exec_command = ide_exec_command; hwif->read_status = ide_read_status; + hwif->read_altstatus = ide_read_altstatus; hwif->read_sff_dma_status = ide_read_sff_dma_status; hwif->tf_load = ide_tf_load; @@ -511,7 +520,7 @@ int drive_is_ready (ide_drive_t *drive) * about possible isa-pnp and pci-pnp issues yet. */ if (hwif->io_ports.ctl_addr) - stat = ide_read_altstatus(drive); + stat = hwif->read_altstatus(hwif); else /* Note: this may clear a pending IRQ!! */ stat = hwif->read_status(hwif); @@ -724,7 +733,7 @@ int ide_driveid_update(ide_drive_t *drive) } msleep(50); /* give drive a breather */ - stat = ide_read_altstatus(drive); + stat = hwif->read_altstatus(hwif); } while (stat & BUSY_STAT); msleep(50); /* wait for IRQ and DRQ_STAT */ diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index c42fcfedcbf6..fe14d576ef01 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -275,7 +275,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) msleep(50); if (io_ports->ctl_addr) { - a = ide_read_altstatus(drive); + a = hwif->read_altstatus(hwif); s = hwif->read_status(hwif); if ((a ^ s) & ~INDEX_STAT) /* ancient Seagate drives, broken interfaces */ @@ -306,7 +306,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) } /* give drive a breather */ msleep(50); - s = use_altstatus ? ide_read_altstatus(drive) + s = use_altstatus ? hwif->read_altstatus(hwif) : hwif->read_status(hwif); } while (s & BUSY_STAT); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 3d72a5e03f3d..a89dc4780786 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -139,6 +139,11 @@ static u8 scc_read_status(ide_hwif_t *hwif) return (u8)in_be32((void *)hwif->io_ports.status_addr); } +static u8 scc_read_altstatus(ide_hwif_t *hwif) +{ + return (u8)in_be32((void *)hwif->io_ports.ctl_addr); +} + static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) { return (u8)in_be32((void *)(hwif->dma_base + 4)); @@ -794,6 +799,7 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) hwif->exec_command = scc_exec_command; hwif->read_status = scc_read_status; + hwif->read_altstatus = scc_read_altstatus; hwif->read_sff_dma_status = scc_read_sff_dma_status; hwif->tf_load = scc_tf_load; diff --git a/include/linux/ide.h b/include/linux/ide.h index a23ae25d7da8..e5fa5e868d67 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -491,6 +491,7 @@ typedef struct hwif_s { void (*exec_command)(struct hwif_s *, u8); u8 (*read_status)(struct hwif_s *); + u8 (*read_altstatus)(struct hwif_s *); u8 (*read_sff_dma_status)(struct hwif_s *); void (*tf_load)(ide_drive_t *, struct ide_task_s *); @@ -1363,13 +1364,6 @@ static inline void ide_set_irq(ide_drive_t *drive, int on) hwif->io_ports.ctl_addr); } -static inline u8 ide_read_altstatus(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - - return hwif->INB(hwif->io_ports.ctl_addr); -} - static inline u8 ide_read_error(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; -- cgit From 6e6afb3b7401f0181da74a1add57f126946b43e6 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:52 +0200 Subject: ide: add ->set_irq method Add ->set_irq method for setting nIEN bit of ATA Device Control register and use it instead of ide_set_irq(). While at it: * Use ->set_irq in init_irq() and do_reset1(). * Don't use HWIF() macro in ide_check_pm_state(). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-io.c | 14 ++++++++------ drivers/ide/ide-iops.c | 37 +++++++++++++++++++++++++++---------- drivers/ide/ide-probe.c | 9 ++++----- drivers/ide/ide-taskfile.c | 2 +- drivers/ide/pci/scc_pata.c | 19 +++++++++++++++++++ drivers/ide/ppc/pmac.c | 17 +++++++++++++++++ include/linux/ide.h | 10 ++-------- 7 files changed, 78 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index fdc221ce9920..bbd7bd4c48ee 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -746,16 +746,17 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) * the bus may be broken enough to walk on our toes at this * point. */ + ide_hwif_t *hwif = drive->hwif; int rc; #ifdef DEBUG_PM printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name); #endif - rc = ide_wait_not_busy(HWIF(drive), 35000); + rc = ide_wait_not_busy(hwif, 35000); if (rc) printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); SELECT_DRIVE(drive); - ide_set_irq(drive, 1); - rc = ide_wait_not_busy(HWIF(drive), 100000); + hwif->set_irq(hwif, 1); + rc = ide_wait_not_busy(hwif, 100000); if (rc) printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); } @@ -1041,7 +1042,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) * quirk_list may not like intr setups/cleanups */ if (drive->quirk_list != 1) - ide_set_irq(drive, 0); + hwif->set_irq(hwif, 0); } hwgroup->hwif = hwif; hwgroup->drive = drive; @@ -1519,6 +1520,7 @@ EXPORT_SYMBOL(ide_do_drive_cmd); void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma) { + ide_hwif_t *hwif = drive->hwif; ide_task_t task; memset(&task, 0, sizeof(task)); @@ -1529,9 +1531,9 @@ void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma) task.tf.lbah = (bcount >> 8) & 0xff; ide_tf_dump(drive->name, &task.tf); - ide_set_irq(drive, 1); + hwif->set_irq(hwif, 1); SELECT_MASK(drive, 0); - drive->hwif->tf_load(drive, &task); + hwif->tf_load(drive, &task); } EXPORT_SYMBOL_GPL(ide_pktcmd_tf_load); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index e106954e13f9..41ec53f329fa 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -135,6 +135,23 @@ static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) return inb(hwif->dma_base + ATA_DMA_STATUS); } +static void ide_set_irq(ide_hwif_t *hwif, int on) +{ + u8 ctl = ATA_DEVCTL_OBS; + + if (on == 4) { /* hack for SRST */ + ctl |= 4; + on &= ~4; + } + + ctl |= on ? 0 : 2; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); + else + outb(ctl, hwif->io_ports.ctl_addr); +} + static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; @@ -360,6 +377,8 @@ void default_hwif_transport(ide_hwif_t *hwif) hwif->read_altstatus = ide_read_altstatus; hwif->read_sff_dma_status = ide_read_sff_dma_status; + hwif->set_irq = ide_set_irq; + hwif->tf_load = ide_tf_load; hwif->tf_read = ide_tf_read; @@ -722,7 +741,7 @@ int ide_driveid_update(ide_drive_t *drive) */ SELECT_MASK(drive, 1); - ide_set_irq(drive, 0); + hwif->set_irq(hwif, 0); msleep(50); hwif->exec_command(hwif, WIN_IDENTIFY); timeout = jiffies + WAIT_WORSTCASE; @@ -808,12 +827,12 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) SELECT_DRIVE(drive); SELECT_MASK(drive, 0); udelay(1); - ide_set_irq(drive, 0); + hwif->set_irq(hwif, 0); hwif->OUTB(speed, io_ports->nsect_addr); hwif->OUTB(SETFEATURES_XFER, io_ports->feature_addr); hwif->exec_command(hwif, WIN_SETFEATURES); if (drive->quirk_list == 2) - ide_set_irq(drive, 1); + hwif->set_irq(hwif, 1); error = __ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT|ERR_STAT, @@ -1129,7 +1148,6 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) ide_hwgroup_t *hwgroup; struct ide_io_ports *io_ports; const struct ide_port_ops *port_ops; - u8 ctl; spin_lock_irqsave(&ide_lock, flags); hwif = HWIF(drive); @@ -1174,16 +1192,15 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) * immediate interrupt due to the edge transition it produces. * This single interrupt gives us a "fast poll" for drives that * recover from reset very quickly, saving us the first 50ms wait time. + * + * TODO: add ->softreset method and stop abusing ->set_irq */ /* set SRST and nIEN */ - hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS | 6, io_ports->ctl_addr); + hwif->set_irq(hwif, 4); /* more than enough time */ udelay(10); - if (drive->quirk_list == 2) - ctl = ATA_DEVCTL_OBS; /* clear SRST and nIEN */ - else - ctl = ATA_DEVCTL_OBS | 2; /* clear SRST, leave nIEN */ - hwif->OUTBSYNC(hwif, ctl, io_ports->ctl_addr); + /* clear SRST, leave nIEN (unless device is on the quirk list) */ + hwif->set_irq(hwif, drive->quirk_list == 2); /* more than enough time */ udelay(10); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index fe14d576ef01..475bd7263184 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -361,7 +361,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) autoprobe = 1; cookie = probe_irq_on(); } - ide_set_irq(drive, autoprobe); + hwif->set_irq(hwif, autoprobe); } retval = actual_try_to_identify(drive, cmd); @@ -369,7 +369,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) if (autoprobe) { int irq; - ide_set_irq(drive, 0); + hwif->set_irq(hwif, 0); /* clear drive IRQ */ (void)hwif->read_status(hwif); udelay(5); @@ -709,7 +709,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) /* Ignore disks that we will not probe for later. */ if (!drive->noprobe || drive->present) { SELECT_DRIVE(drive); - ide_set_irq(drive, 1); + hwif->set_irq(hwif, 1); mdelay(2); rc = ide_wait_not_busy(hwif, 35000); if (rc) @@ -1066,8 +1066,7 @@ static int init_irq (ide_hwif_t *hwif) sa = IRQF_SHARED; if (io_ports->ctl_addr) - /* clear nIEN */ - hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS, io_ports->ctl_addr); + hwif->set_irq(hwif, 1); if (request_irq(hwif->irq,&ide_intr,sa,hwif->name,hwgroup)) goto out_unlink; diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index fc2b3957afac..ea345369553e 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -80,7 +80,7 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, tf); - ide_set_irq(drive, 1); + hwif->set_irq(hwif, 1); SELECT_MASK(drive, 0); hwif->tf_load(drive, task); } diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index a89dc4780786..727eda6db76c 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -149,6 +149,23 @@ static u8 scc_read_sff_dma_status(ide_hwif_t *hwif) return (u8)in_be32((void *)(hwif->dma_base + 4)); } +static void scc_set_irq(ide_hwif_t *hwif, int on) +{ + u8 ctl = ATA_DEVCTL_OBS; + + if (on == 4) { /* hack for SRST */ + ctl |= 4; + on &= ~4; + } + + ctl |= on ? 0 : 2; + + out_be32((void *)hwif->io_ports.ctl_addr, ctl); + eieio(); + in_be32((void *)(hwif->dma_base + 0x01c)); + eieio(); +} + static void scc_ide_insw(unsigned long port, void *addr, u32 count) { u16 *ptr = (u16 *)addr; @@ -802,6 +819,8 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) hwif->read_altstatus = scc_read_altstatus; hwif->read_sff_dma_status = scc_read_sff_dma_status; + hwif->set_irq = scc_set_irq; + hwif->tf_load = scc_tf_load; hwif->tf_read = scc_tf_read; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index 7c3a84f8fbed..a0d66480a797 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -502,6 +502,22 @@ static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd) + IDE_TIMING_CONFIG)); } +static void pmac_set_irq(ide_hwif_t *hwif, int on) +{ + u8 ctl = ATA_DEVCTL_OBS; + + if (on == 4) { /* hack for SRST */ + ctl |= 4; + on &= ~4; + } + + ctl |= on ? 0 : 2; + + writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); + (void)readl((void __iomem *)(hwif->io_ports.data_addr + + IDE_TIMING_CONFIG)); +} + /* * Old tuning functions (called on hdparm -p), sets up drive PIO timings */ @@ -1100,6 +1116,7 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) return -ENOENT; hwif->exec_command = pmac_exec_command; + hwif->set_irq = pmac_set_irq; /* Setup MMIO ops */ default_hwif_mmiops(hwif); diff --git a/include/linux/ide.h b/include/linux/ide.h index e5fa5e868d67..ae93f89e4448 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -494,6 +494,8 @@ typedef struct hwif_s { u8 (*read_altstatus)(struct hwif_s *); u8 (*read_sff_dma_status)(struct hwif_s *); + void (*set_irq)(struct hwif_s *, int); + void (*tf_load)(ide_drive_t *, struct ide_task_s *); void (*tf_read)(ide_drive_t *, struct ide_task_s *); @@ -1356,14 +1358,6 @@ static inline ide_drive_t *ide_get_paired_drive(ide_drive_t *drive) return &hwif->drives[(drive->dn ^ 1) & 1]; } -static inline void ide_set_irq(ide_drive_t *drive, int on) -{ - ide_hwif_t *hwif = drive->hwif; - - hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS | (on ? 0 : 2), - hwif->io_ports.ctl_addr); -} - static inline u8 ide_read_error(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; -- cgit From 92eb43800a3c1300bd5cb8a2a27e6f2a84f7042e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:53 +0200 Subject: ide: use ->tf_read in ide_read_error() * Add IDE_TFLAG_IN_FEATURE taskfile flag for reading Feature register and handle it in ->tf_read. * Convert ide_read_error() to use ->tf_read instead of ->INB, then uninline and export it. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/h8300/ide-h8300.c | 2 ++ drivers/ide/ide-iops.c | 15 +++++++++++++++ drivers/ide/pci/ns87415.c | 2 ++ drivers/ide/pci/scc_pata.c | 2 ++ include/linux/ide.h | 10 +++------- 5 files changed, 24 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index a71433b3d0f3..548a2bf232c9 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -100,6 +100,8 @@ static void h8300_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 1983b353eb16..113db8744736 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -241,6 +241,8 @@ static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ tf_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = tf_inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = tf_inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) @@ -390,6 +392,19 @@ void default_hwif_transport(ide_hwif_t *hwif) hwif->output_data = ata_output_data; } +u8 ide_read_error(ide_drive_t *drive) +{ + ide_task_t task; + + memset(&task, 0, sizeof(task)); + task.tf_flags = IDE_TFLAG_IN_FEATURE; + + drive->hwif->tf_read(drive, &task); + + return task.tf.error; +} +EXPORT_SYMBOL_GPL(ide_read_error); + void ide_fix_driveid (struct hd_driveid *id) { #ifndef __LITTLE_ENDIAN diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index b20e5f01ac89..b9bb8428b35e 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -88,6 +88,8 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 727eda6db76c..c110f359b03e 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -741,6 +741,8 @@ static void scc_tf_read(ide_drive_t *drive, ide_task_t *task) /* be sure we're looking at the low order bits */ scc_ide_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = scc_ide_inb(io_ports->feature_addr); if (task->tf_flags & IDE_TFLAG_IN_NSECT) tf->nsect = scc_ide_inb(io_ports->nsect_addr); if (task->tf_flags & IDE_TFLAG_IN_LBAL) diff --git a/include/linux/ide.h b/include/linux/ide.h index ae93f89e4448..7890768d03ed 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -889,6 +889,7 @@ enum { IDE_TFLAG_IN_HOB = IDE_TFLAG_IN_HOB_FEATURE | IDE_TFLAG_IN_HOB_NSECT | IDE_TFLAG_IN_HOB_LBA, + IDE_TFLAG_IN_FEATURE = (1 << 1), IDE_TFLAG_IN_NSECT = (1 << 25), IDE_TFLAG_IN_LBAL = (1 << 26), IDE_TFLAG_IN_LBAM = (1 << 27), @@ -956,6 +957,8 @@ void ide_tf_dump(const char *, struct ide_taskfile *); extern void SELECT_DRIVE(ide_drive_t *); void SELECT_MASK(ide_drive_t *, int); +u8 ide_read_error(ide_drive_t *); + extern int drive_is_ready(ide_drive_t *); void ide_pktcmd_tf_load(ide_drive_t *, u32, u16, u8); @@ -1357,11 +1360,4 @@ static inline ide_drive_t *ide_get_paired_drive(ide_drive_t *drive) return &hwif->drives[(drive->dn ^ 1) & 1]; } - -static inline u8 ide_read_error(ide_drive_t *drive) -{ - ide_hwif_t *hwif = drive->hwif; - - return hwif->INB(hwif->io_ports.error_addr); -} #endif /* _IDE_H */ -- cgit From 1823649b5abb77ffe638178bc5253249d3ecd17d Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:54 +0200 Subject: ide: add ide_read_bcount_and_ireason() helper Add ide_read_bcount_and_ireason() helper and use it instead of ->INB in {cdrom_newpc,ide_pc}_intr(). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 6 ++---- drivers/ide/ide-cd.c | 12 ++++-------- drivers/ide/ide-iops.c | 15 +++++++++++++++ include/linux/ide.h | 1 + 4 files changed, 22 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index bfab5c4afc6a..f17a00ccbe96 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -107,11 +107,9 @@ cmd_finished: ide_dma_off(drive); return ide_do_reset(drive); } - /* Get the number of bytes to transfer on this interrupt. */ - bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) | - hwif->INB(hwif->io_ports.lbam_addr); - ireason = hwif->INB(hwif->io_ports.nsect_addr); + /* Get the number of bytes to transfer on this interrupt. */ + ide_read_bcount_and_ireason(drive, &bcount, &ireason); if (ireason & CD) { printk(KERN_ERR "%s: CoD != 0 in %s\n", drive->name, __func__); diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 82879a1a89e5..563a380d0e7a 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -895,10 +895,11 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) struct request *rq = HWGROUP(drive)->rq; xfer_func_t *xferfunc; ide_expiry_t *expiry = NULL; - int dma_error = 0, dma, stat, ireason, len, thislen, uptodate = 0; + int dma_error = 0, dma, stat, thislen, uptodate = 0; int write = (rq_data_dir(rq) == WRITE) ? 1 : 0; unsigned int timeout; - u8 lowcyl, highcyl; + u16 len; + u8 ireason; /* check for errors */ dma = info->dma; @@ -926,12 +927,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) goto end_request; } - /* ok we fall to pio :/ */ - ireason = hwif->INB(hwif->io_ports.nsect_addr) & 0x3; - lowcyl = hwif->INB(hwif->io_ports.lbam_addr); - highcyl = hwif->INB(hwif->io_ports.lbah_addr); - - len = lowcyl + (256 * highcyl); + ide_read_bcount_and_ireason(drive, &len, &ireason); thislen = blk_fs_request(rq) ? len : rq->data_len; if (thislen > len) diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 113db8744736..cb11c7861a50 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -405,6 +405,21 @@ u8 ide_read_error(ide_drive_t *drive) } EXPORT_SYMBOL_GPL(ide_read_error); +void ide_read_bcount_and_ireason(ide_drive_t *drive, u16 *bcount, u8 *ireason) +{ + ide_task_t task; + + memset(&task, 0, sizeof(task)); + task.tf_flags = IDE_TFLAG_IN_LBAH | IDE_TFLAG_IN_LBAM | + IDE_TFLAG_IN_NSECT; + + drive->hwif->tf_read(drive, &task); + + *bcount = (task.tf.lbah << 8) | task.tf.lbam; + *ireason = task.tf.nsect & 3; +} +EXPORT_SYMBOL_GPL(ide_read_bcount_and_ireason); + void ide_fix_driveid (struct hd_driveid *id) { #ifndef __LITTLE_ENDIAN diff --git a/include/linux/ide.h b/include/linux/ide.h index 7890768d03ed..fd05758e6995 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -958,6 +958,7 @@ extern void SELECT_DRIVE(ide_drive_t *); void SELECT_MASK(ide_drive_t *, int); u8 ide_read_error(ide_drive_t *); +void ide_read_bcount_and_ireason(ide_drive_t *, u16 *, u8 *); extern int drive_is_ready(ide_drive_t *); -- cgit From 761052e676372465fdeb97c148d5a4b0790fa8a0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:54 +0200 Subject: ide: remove ->INB, ->OUTB and ->OUTBSYNC methods * Remove no longer needed ->INB, ->OUTB and ->OUTBSYNC methods. Then: * Remove no longer used default_hwif_[mm]iops() and ide_[mm_]outbsync(). * Cleanup SuperIO handling in ns87415.c. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 5 ----- drivers/ide/arm/palm_bk3710.c | 2 -- drivers/ide/arm/rapide.c | 2 -- drivers/ide/h8300/ide-h8300.c | 2 -- drivers/ide/ide-iops.c | 28 ------------------------ drivers/ide/ide.c | 1 - drivers/ide/legacy/ide_platform.c | 4 +--- drivers/ide/mips/swarm.c | 3 --- drivers/ide/pci/ns87415.c | 46 +++++++++++---------------------------- drivers/ide/pci/scc_pata.c | 12 ---------- drivers/ide/pci/sgiioc4.c | 3 --- drivers/ide/pci/siimage.c | 2 +- drivers/ide/ppc/pmac.c | 13 ----------- include/linux/ide.h | 7 ------ 14 files changed, 15 insertions(+), 115 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index f73422dd8474..0fd01d630f12 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -469,8 +469,6 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) if (!hwif) return -ENODEV; - default_hwif_mmiops(hwif); - state->hwif[0] = hwif; ecard_set_drvdata(ec, state); @@ -547,14 +545,11 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) return -ENODEV; hwif->chipset = ide_acorn; - default_hwif_mmiops(hwif); idx[0] = hwif->index; mate = ide_find_port(); if (mate) { - default_hwif_mmiops(mate); - hws[1] = &hw[1]; idx[1] = mate->index; } diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 0229d794d909..545563bc7e23 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -399,8 +399,6 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) i = hwif->index; - default_hwif_mmiops(hwif); - idx[0] = i; ide_device_add(idx, &palm_bk3710_port_info, hws); diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index 01896f6e8acf..a45c2f694949 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -59,8 +59,6 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) goto release; } - default_hwif_mmiops(hwif); - idx[0] = hwif->index; ide_device_add(idx, &rapide_port_info, hws); diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 548a2bf232c9..0795d6554913 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -171,8 +171,6 @@ static inline void hw_setup(hw_regs_t *hw) static inline void hwif_setup(ide_hwif_t *hwif) { - default_hwif_iops(hwif); - hwif->tf_load = h8300_tf_load; hwif->tf_read = h8300_tf_read; diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index cb11c7861a50..6c54fe1f9841 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -42,18 +42,6 @@ static void ide_outb (u8 val, unsigned long port) outb(val, port); } -static void ide_outbsync(ide_hwif_t *hwif, u8 addr, unsigned long port) -{ - outb(addr, port); -} - -void default_hwif_iops (ide_hwif_t *hwif) -{ - hwif->OUTB = ide_outb; - hwif->OUTBSYNC = ide_outbsync; - hwif->INB = ide_inb; -} - /* * MMIO operations, typically used for SATA controllers */ @@ -68,22 +56,6 @@ static void ide_mm_outb (u8 value, unsigned long port) writeb(value, (void __iomem *) port); } -static void ide_mm_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) -{ - writeb(value, (void __iomem *) port); -} - -void default_hwif_mmiops (ide_hwif_t *hwif) -{ - hwif->OUTB = ide_mm_outb; - /* Most systems will need to override OUTBSYNC, alas however - this one is controller specific! */ - hwif->OUTBSYNC = ide_mm_outbsync; - hwif->INB = ide_mm_inb; -} - -EXPORT_SYMBOL(default_hwif_mmiops); - void SELECT_DRIVE (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index b6018f7b0907..434dd02a4bdc 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -101,7 +101,6 @@ void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) init_completion(&hwif->gendev_rel_comp); - default_hwif_iops(hwif); default_hwif_transport(hwif); ide_port_init_devices_data(hwif); diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index 609da0d43196..3d71e336a221 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -103,10 +103,8 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start); hw.dev = &pdev->dev; - if (mmio) { + if (mmio) d.host_flags |= IDE_HFLAG_MMIO; - default_hwif_mmiops(hwif); - } idx[0] = hwif->index; diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index 6da6844d2d8c..c1ffb83a2de7 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -120,9 +120,6 @@ static int __devinit swarm_ide_probe(struct device *dev) if (hwif == NULL) goto err; - /* Setup MMIO ops. */ - default_hwif_mmiops(hwif); - idx[0] = hwif->index; ide_device_add(idx, &swarm_port_info, hws); diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index b9bb8428b35e..02fd3a877afa 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -28,10 +28,6 @@ */ #include -static unsigned long superio_ide_status[2]; -static unsigned long superio_ide_select[2]; -static unsigned long superio_ide_dma_status[2]; - #define SUPERIO_IDE_MAX_RETRIES 25 /* Because of a defect in Super I/O, all reads of the PCI DMA status @@ -40,27 +36,18 @@ static unsigned long superio_ide_dma_status[2]; */ static u8 superio_ide_inb (unsigned long port) { - if (port == superio_ide_status[0] || - port == superio_ide_status[1] || - port == superio_ide_select[0] || - port == superio_ide_select[1] || - port == superio_ide_dma_status[0] || - port == superio_ide_dma_status[1]) { - u8 tmp; - int retries = SUPERIO_IDE_MAX_RETRIES; - - /* printk(" [ reading port 0x%x with retry ] ", port); */ + u8 tmp; + int retries = SUPERIO_IDE_MAX_RETRIES; - do { - tmp = inb(port); - if (tmp == 0) - udelay(50); - } while (tmp == 0 && retries-- > 0); + /* printk(" [ reading port 0x%x with retry ] ", port); */ - return tmp; - } + do { + tmp = inb(port); + if (tmp == 0) + udelay(50); + } while (tmp == 0 && retries-- > 0); - return inb(port); + return tmp; } static u8 superio_read_status(ide_hwif_t *hwif) @@ -120,27 +107,20 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) static void __devinit superio_ide_init_iops (struct hwif_s *hwif) { struct pci_dev *pdev = to_pci_dev(hwif->dev); - u32 base, dmabase; + u32 dma_stat; u8 port = hwif->channel, tmp; - base = pci_resource_start(pdev, port * 2) & ~3; - dmabase = pci_resource_start(pdev, 4) & ~3; - - superio_ide_status[port] = base + 7; - superio_ide_select[port] = base + 6; - superio_ide_dma_status[port] = dmabase + (!port ? 2 : 0xa); + dma_stat = (pci_resource_start(pdev, 4) & ~3) + (!port ? 2 : 0xa); /* Clear error/interrupt, enable dma */ - tmp = superio_ide_inb(superio_ide_dma_status[port]); - outb(tmp | 0x66, superio_ide_dma_status[port]); + tmp = superio_ide_inb(dma_stat); + outb(tmp | 0x66, dma_stat); hwif->read_status = superio_read_status; hwif->read_sff_dma_status = superio_read_sff_dma_status; hwif->tf_read = superio_tf_read; - /* We need to override inb to workaround a SuperIO errata */ - hwif->INB = superio_ide_inb; } static void __devinit init_iops_ns87415(ide_hwif_t *hwif) diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index c110f359b03e..38765d9b0314 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -188,14 +188,6 @@ static void scc_ide_outb(u8 addr, unsigned long port) out_be32((void*)port, addr); } -static void scc_ide_outbsync(ide_hwif_t *hwif, u8 addr, unsigned long port) -{ - out_be32((void*)port, addr); - eieio(); - in_be32((void*)(hwif->dma_base + 0x01c)); - eieio(); -} - static void scc_ide_outsw(unsigned long port, void *addr, u32 count) { @@ -829,10 +821,6 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) hwif->input_data = scc_input_data; hwif->output_data = scc_output_data; - hwif->INB = scc_ide_inb; - hwif->OUTB = scc_ide_outb; - hwif->OUTBSYNC = scc_ide_outbsync; - hwif->dma_base = dma_base; hwif->config_data = ports->ctl; } diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 3005a6334a58..86f7c4901837 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -623,9 +623,6 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) if (hwif == NULL) goto err; - /* The IOC4 uses MMIO rather than Port IO. */ - default_hwif_mmiops(hwif); - /* Initializing chipset IRQ Registers */ writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 21d7137f7d6c..5965a35d94ae 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -601,7 +601,7 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif) * Fill in the basic hwif bits */ hwif->host_flags |= IDE_HFLAG_MMIO; - default_hwif_mmiops(hwif); + hwif->hwif_data = addr; /* diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index a0d66480a797..ed073c6635a8 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -486,15 +486,6 @@ pmac_ide_do_update_timings(ide_drive_t *drive) pmac_ide_selectproc(drive); } -static void pmac_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port) -{ - u32 tmp; - - writeb(value, (void __iomem *) port); - tmp = readl((void __iomem *)(hwif->io_ports.data_addr - + IDE_TIMING_CONFIG)); -} - static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd) { writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); @@ -1118,10 +1109,6 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) hwif->exec_command = pmac_exec_command; hwif->set_irq = pmac_set_irq; - /* Setup MMIO ops */ - default_hwif_mmiops(hwif); - hwif->OUTBSYNC = pmac_outbsync; - idx[0] = hwif->index; ide_device_add(idx, &d, hws); diff --git a/include/linux/ide.h b/include/linux/ide.h index fd05758e6995..0544ff7d9afc 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -504,11 +504,6 @@ typedef struct hwif_s { void (*ide_dma_clear_irq)(ide_drive_t *drive); - void (*OUTB)(u8 addr, unsigned long port); - void (*OUTBSYNC)(struct hwif_s *hwif, u8 addr, unsigned long port); - - u8 (*INB)(unsigned long port); - /* dma physical region descriptor table (cpu view) */ unsigned int *dmatable_cpu; /* dma physical region descriptor table (dma view) */ @@ -1027,8 +1022,6 @@ static inline int ide_hwif_setup_dma(ide_hwif_t *hwif, } #endif -extern void default_hwif_iops(ide_hwif_t *); -extern void default_hwif_mmiops(ide_hwif_t *); extern void default_hwif_transport(ide_hwif_t *); typedef struct ide_pci_enablebit_s { -- cgit From ba4b2e607e4e9eaa929935325dafd5c86d3b5262 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:55 +0200 Subject: ide: remove dead Virtual DMA support Lets remove dead Virtual DMA support for now so it doesn't clutter core IDE code (it can be bring back when there is a need for it): * Remove IDE_HFLAG_VDMA host flag. * Remove ide_drive_t.vdma flag. * cs5520.c: remove stale FIXMEs, cs5520_dma_host_set() and cs5520_dma_ops (also there is no longer a need to set IDE_HFLAG_NO_ATAPI_DMA). There should be no functional changes caused by this patch. Cc: TAKADA Yoshihito Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-disk.c | 2 +- drivers/ide/ide-dma.c | 5 ++--- drivers/ide/ide-iops.c | 3 +-- drivers/ide/pci/cs5520.c | 30 ------------------------------ include/linux/ide.h | 3 --- 5 files changed, 4 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 3a2e80237c10..df5fe5756871 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -158,7 +158,7 @@ static void ide_tf_set_cmd(ide_drive_t *drive, ide_task_t *task, u8 dma) write = (task->tf_flags & IDE_TFLAG_WRITE) ? 1 : 0; if (dma) - index = drive->vdma ? 4 : 8; + index = 8; else index = drive->mult_count ? 0 : 4; diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index ecf60dbbe3e9..e72112efab9a 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -756,9 +756,8 @@ static int ide_tune_dma(ide_drive_t *drive) static int ide_dma_check(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - int vdma = (hwif->host_flags & IDE_HFLAG_VDMA)? 1 : 0; - if (!vdma && ide_tune_dma(drive)) + if (ide_tune_dma(drive)) return 0; /* TODO: always do PIO fallback */ @@ -767,7 +766,7 @@ static int ide_dma_check(ide_drive_t *drive) ide_set_max_pio(drive); - return vdma ? 0 : -1; + return -1; } int ide_id_dma_bug(ide_drive_t *drive) diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 6c54fe1f9841..c9d15be4c48d 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -866,8 +866,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) skip: #ifdef CONFIG_BLK_DEV_IDEDMA - if ((speed >= XFER_SW_DMA_0 || (hwif->host_flags & IDE_HFLAG_VDMA)) && - drive->using_dma) + if (speed >= XFER_SW_DMA_0 && drive->using_dma) hwif->dma_ops->dma_host_set(drive, 1); else if (hwif->dma_ops) /* check if host supports DMA */ ide_dma_off_quietly(drive); diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index a13f2bc0665c..d30cb4d99c45 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -62,8 +62,6 @@ static void cs5520_set_pio_mode(ide_drive_t *drive, const u8 pio) struct pci_dev *pdev = to_pci_dev(hwif->dev); int controller = drive->dn > 1 ? 1 : 0; - /* FIXME: if DMA = 1 do we need to set the DMA bit here ? */ - /* 8bit CAT/CRT - 8bit command timing for channel */ pci_write_config_byte(pdev, 0x62 + controller, (cs5520_pio_clocks[pio].recovery << 4) | @@ -89,45 +87,17 @@ static void cs5520_set_dma_mode(ide_drive_t *drive, const u8 speed) cs5520_set_pio_mode(drive, 0); } -/* - * We wrap the DMA activate to set the vdma flag. This is needed - * so that the IDE DMA layer issues PIO not DMA commands over the - * DMA channel - * - * ATAPI is harder so disable it for now using IDE_HFLAG_NO_ATAPI_DMA - */ - -static void cs5520_dma_host_set(ide_drive_t *drive, int on) -{ - drive->vdma = on; - ide_dma_host_set(drive, on); -} - static const struct ide_port_ops cs5520_port_ops = { .set_pio_mode = cs5520_set_pio_mode, .set_dma_mode = cs5520_set_dma_mode, }; -static const struct ide_dma_ops cs5520_dma_ops = { - .dma_host_set = cs5520_dma_host_set, - .dma_setup = ide_dma_setup, - .dma_exec_cmd = ide_dma_exec_cmd, - .dma_start = ide_dma_start, - .dma_end = __ide_dma_end, - .dma_test_irq = ide_dma_test_irq, - .dma_lost_irq = ide_dma_lost_irq, - .dma_timeout = ide_dma_timeout, -}; - -/* FIXME: VDMA is disabled because it caused system hangs */ #define DECLARE_CS_DEV(name_str) \ { \ .name = name_str, \ .port_ops = &cs5520_port_ops, \ - .dma_ops = &cs5520_dma_ops, \ .host_flags = IDE_HFLAG_ISA_PORTS | \ IDE_HFLAG_CS5520 | \ - IDE_HFLAG_NO_ATAPI_DMA | \ IDE_HFLAG_ABUSE_SET_DMA_MODE, \ .pio_mask = ATA_PIO4, \ } diff --git a/include/linux/ide.h b/include/linux/ide.h index 0544ff7d9afc..3f2889400250 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -355,7 +355,6 @@ typedef struct ide_drive_s { unsigned nodma : 1; /* disallow DMA */ unsigned remap_0_to_1 : 1; /* 0=noremap, 1=remap 0->1 (for EZDrive) */ unsigned blocked : 1; /* 1=powermanagment told us not to do anything, so sleep nicely */ - unsigned vdma : 1; /* 1=doing PIO over DMA 0=doing normal DMA */ unsigned scsi : 1; /* 0=default, 1=ide-scsi emulation */ unsigned sleeping : 1; /* 1=sleeping & sleep field valid */ unsigned post_reset : 1; @@ -1097,8 +1096,6 @@ enum { IDE_HFLAG_NO_IO_32BIT = (1 << 30), /* never unmask IRQs */ IDE_HFLAG_NO_UNMASK_IRQS = (1 << 31), - /* host uses VDMA (disabled for now) */ - IDE_HFLAG_VDMA = 0, }; #ifdef CONFIG_BLK_DEV_OFFBOARD -- cgit From 3b2a5c7149ee4af4aff8fee953f66fc846d92cea Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:56 +0200 Subject: ide: filter out "default" transfer mode values in set_xfer_rate() * Filter out "default" transfer mode values (0x00 - default PIO mode, 0x01 - default PIO mode w/ IORDY disabled) in write handler for obsoleted /proc/ide/hd?/settings:current_speed setting. Allowing "default" transfer mode values is a dangerous thing to do as we don't support programming controller to the "default" transfer mode and devices often use different values for the default and maximum PIO mode (i.e. PIO2 default and PIO4 maximum) so the controller will stay programmed for higher PIO mode while device will use the lower PIO mode. There is no functionality loss as by using special IOCTLs device can still be programmed to "default" transfer modes (it is only useful for debugging/testing purposes anyway). * Remove no longer needed IDE_HFLAG_ABUSE_SET_DMA_MODE host flag, it was previously used by few host drivers to program the controller to PIO0 timings for "default" transfer mode == 0x01 (although some host drivers would program invalid PIO timings instead). * Cleanup ide_set_xfer_rate() and add BUG_ON(). Suggested-by: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-lib.c | 15 ++------------- drivers/ide/ide-proc.c | 4 ++-- drivers/ide/pci/aec62xx.c | 5 ----- drivers/ide/pci/amd74xx.c | 1 - drivers/ide/pci/cs5520.c | 3 +-- drivers/ide/pci/cs5535.c | 3 +-- drivers/ide/pci/hpt34x.c | 1 - drivers/ide/pci/hpt366.c | 1 - drivers/ide/pci/pdc202xx_old.c | 1 - drivers/ide/pci/serverworks.c | 4 +--- drivers/ide/pci/tc86c001.c | 3 +-- drivers/ide/pci/via82cxxx.c | 1 - include/linux/ide.h | 1 - 13 files changed, 8 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 13af72f09ec4..7ac44d515470 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -266,22 +266,11 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) rate = ide_rate_filter(drive, rate); + BUG_ON(rate < XFER_PIO_0); + if (rate >= XFER_PIO_0 && rate <= XFER_PIO_5) return ide_set_pio_mode(drive, rate); - /* - * TODO: transfer modes 0x00-0x07 passed from the user-space are - * currently handled here which needs fixing (please note that such - * case could happen iff the transfer mode has already been set on - * the device by ide-proc.c::set_xfer_rate()). - */ - if (rate < XFER_PIO_0) { - if (hwif->host_flags & IDE_HFLAG_ABUSE_SET_DMA_MODE) - return ide_set_dma_mode(drive, rate); - else - return ide_config_drive_speed(drive, rate); - } - return ide_set_dma_mode(drive, rate); } diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index 54d57a15d59e..151c91e933da 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -345,7 +345,7 @@ static int set_xfer_rate (ide_drive_t *drive, int arg) ide_task_t task; int err; - if (arg < 0 || (arg > 1 && arg < XFER_PIO_0) || arg > XFER_UDMA_6) + if (arg < XFER_PIO_0 || arg > XFER_UDMA_6) return -EINVAL; memset(&task, 0, sizeof(task)); @@ -357,7 +357,7 @@ static int set_xfer_rate (ide_drive_t *drive, int arg) err = ide_no_data_taskfile(drive, &task); - if (!err && arg) { + if (!err) { ide_set_xfer_rate(drive, (u8) arg); ide_driveid_update(drive); } diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index ae7a4329a581..fbc43e121e6b 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -195,7 +195,6 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { .host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_DSC | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, @@ -205,7 +204,6 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { .init_chipset = init_chipset_aec62xx, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_AUTODMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, @@ -216,7 +214,6 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_NON_BOOTABLE, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, @@ -226,7 +223,6 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { .init_chipset = init_chipset_aec62xx, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, @@ -237,7 +233,6 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = { .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, .port_ops = &atp86x_port_ops, .host_flags = IDE_HFLAG_NO_ATAPI_DMA | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index 0bfcdd0e77b3..ef7d971031ee 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -218,7 +218,6 @@ static const struct ide_port_ops amd_port_ops = { #define IDE_HFLAGS_AMD \ (IDE_HFLAG_PIO_NO_BLACKLIST | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_POST_SET_MODE | \ IDE_HFLAG_IO_32BIT | \ IDE_HFLAG_UNMASK_IRQS) diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index d30cb4d99c45..e8e7df1915bf 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -97,8 +97,7 @@ static const struct ide_port_ops cs5520_port_ops = { .name = name_str, \ .port_ops = &cs5520_port_ops, \ .host_flags = IDE_HFLAG_ISA_PORTS | \ - IDE_HFLAG_CS5520 | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE, \ + IDE_HFLAG_CS5520, \ .pio_mask = ATA_PIO4, \ } diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c index dc97c48623f3..5404fe4f701d 100644 --- a/drivers/ide/pci/cs5535.c +++ b/drivers/ide/pci/cs5535.c @@ -171,8 +171,7 @@ static const struct ide_port_ops cs5535_port_ops = { static const struct ide_port_info cs5535_chipset __devinitdata = { .name = "CS5535", .port_ops = &cs5535_port_ops, - .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE | - IDE_HFLAG_ABUSE_SET_DMA_MODE, + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA4, diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index 84c36c117194..9e1d1c4741da 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -123,7 +123,6 @@ static const struct ide_port_ops hpt34x_port_ops = { #define IDE_HFLAGS_HPT34X \ (IDE_HFLAG_NO_ATAPI_DMA | \ IDE_HFLAG_NO_DSC | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_NO_AUTODMA) static const struct ide_port_info hpt34x_chipsets[] __devinitdata = { diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index e5651cefa395..1f1135ce7cd6 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -1409,7 +1409,6 @@ static int __devinit hpt36x_init(struct pci_dev *dev, struct pci_dev *dev2) #define IDE_HFLAGS_HPT3XX \ (IDE_HFLAG_NO_ATAPI_DMA | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_OFF_BOARD) static const struct ide_port_ops hpt3xx_port_ops = { diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index 1c2f9df31129..e54dc653b8c4 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -312,7 +312,6 @@ static void __devinit pdc202ata4_fixup_irq(struct pci_dev *dev, #define IDE_HFLAGS_PDC202XX \ (IDE_HFLAG_ERROR_STOPS_FIFO | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE | \ IDE_HFLAG_OFF_BOARD) static const struct ide_port_ops pdc20246_port_ops = { diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index a1fb20826a5b..127ccb45e261 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -349,9 +349,7 @@ static const struct ide_port_ops svwks_port_ops = { .cable_detect = svwks_cable_detect, }; -#define IDE_HFLAGS_SVWKS \ - (IDE_HFLAG_LEGACY_IRQS | \ - IDE_HFLAG_ABUSE_SET_DMA_MODE) +#define IDE_HFLAGS_SVWKS IDE_HFLAG_LEGACY_IRQS static const struct ide_port_info serverworks_chipsets[] __devinitdata = { { /* 0 */ diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c index a81d47c55ce1..477e19790102 100644 --- a/drivers/ide/pci/tc86c001.c +++ b/drivers/ide/pci/tc86c001.c @@ -206,8 +206,7 @@ static const struct ide_port_info tc86c001_chipset __devinitdata = { .init_hwif = init_hwif_tc86c001, .port_ops = &tc86c001_port_ops, .dma_ops = &tc86c001_dma_ops, - .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_OFF_BOARD | - IDE_HFLAG_ABUSE_SET_DMA_MODE, + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_OFF_BOARD, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA4, diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index e47384c70c40..09dc4803ef9d 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -425,7 +425,6 @@ static const struct ide_port_info via82cxxx_chipset __devinitdata = { .enablebits = { { 0x40, 0x02, 0x02 }, { 0x40, 0x01, 0x01 } }, .port_ops = &via_port_ops, .host_flags = IDE_HFLAG_PIO_NO_BLACKLIST | - IDE_HFLAG_ABUSE_SET_DMA_MODE | IDE_HFLAG_POST_SET_MODE | IDE_HFLAG_IO_32BIT, .pio_mask = ATA_PIO5, diff --git a/include/linux/ide.h b/include/linux/ide.h index 3f2889400250..260e871ae880 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1085,7 +1085,6 @@ enum { IDE_HFLAG_IO_32BIT = (1 << 24), /* unmask IRQs */ IDE_HFLAG_UNMASK_IRQS = (1 << 25), - IDE_HFLAG_ABUSE_SET_DMA_MODE = (1 << 26), /* serialize ports if DMA is possible (for sl82c105) */ IDE_HFLAG_SERIALIZE_DMA = (1 << 27), /* force host out of "simplex" mode */ -- cgit From d6276b5f5cc7508124de291f3ed59c6945c17ae7 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:56 +0200 Subject: ide: add 'config' field to hw_regs_t Add 'config' field to hw_regs_t and use it to set hwif->config_data in ide_init_port_hw(), then convert ide_legacy_init_one() to use hw->config. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 4 +--- drivers/ide/ide.c | 1 + include/linux/ide.h | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 21cea45e9f21..3cc8ade2cc4f 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1689,14 +1689,12 @@ static void ide_legacy_init_one(u8 *idx, hw_regs_t **hws, hw_regs_t *hw, ide_std_init_ports(hw, base, ctl); hw->irq = irq; hw->chipset = d->chipset; + hw->config = config; hwif = ide_find_port_slot(d); if (hwif) { hwif->chipset = hw->chipset; - if (config) - hwif->config_data = config; - hws[port_no] = hw; idx[port_no] = hwif->index; } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 434dd02a4bdc..961f31c648c9 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -286,6 +286,7 @@ void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) hwif->dev = hw->dev; hwif->gendev.parent = hw->parent ? hw->parent : hw->dev; hwif->ack_intr = hw->ack_intr; + hwif->config_data = hw->config; } /* diff --git a/include/linux/ide.h b/include/linux/ide.h index 260e871ae880..e340218b2a5f 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -178,6 +178,7 @@ typedef struct hw_regs_s { ide_ack_intr_t *ack_intr; /* acknowledge interrupt */ hwif_chipset_t chipset; struct device *dev, *parent; + unsigned long config; } hw_regs_t; void ide_init_port_data(struct hwif_s *, unsigned int); -- cgit From 374e042c3e767ac2e5a40b78529220e0b3de793c Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:56 +0200 Subject: ide: add struct ide_tp_ops (take 2) * Add struct ide_tp_ops for transport methods. * Add 'const struct ide_tp_ops *tp_ops' to struct ide_port_info and ide_hwif_t. * Set the default hwif->tp_ops in ide_init_port_data(). * Set host driver specific hwif->tp_ops in ide_init_port(). * Export ide_exec_command(), ide_read_status(), ide_read_altstatus(), ide_read_sff_dma_status(), ide_set_irq(), ide_tf_{load,read}() and ata_{in,out}put_data(). * Convert host drivers and core code to use struct ide_tp_ops. * Remove no longer needed default_hwif_transport(). * Cleanup ide_hwif_t from methods that are now in struct ide_tp_ops. While at it: * Use struct ide_port_info in falconide.c and q40ide.c. * Rename ata_{in,out}put_data() to ide_{in,out}put_data(). v2: * Fix missing convertion in ns87415.c. There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 2 +- drivers/ide/h8300/ide-h8300.c | 26 +++++---- drivers/ide/ide-atapi.c | 13 +++-- drivers/ide/ide-cd.c | 12 ++-- drivers/ide/ide-dma.c | 12 ++-- drivers/ide/ide-floppy.c | 8 +-- drivers/ide/ide-io.c | 32 +++++------ drivers/ide/ide-iops.c | 122 +++++++++++++++++++++++------------------ drivers/ide/ide-lib.c | 2 +- drivers/ide/ide-probe.c | 53 ++++++++++-------- drivers/ide/ide-tape.c | 8 +-- drivers/ide/ide-taskfile.c | 29 ++++++---- drivers/ide/ide.c | 2 +- drivers/ide/legacy/falconide.c | 27 +++++++-- drivers/ide/legacy/q40ide.c | 27 +++++++-- drivers/ide/mips/au1xxx-ide.c | 29 +++++++--- drivers/ide/pci/ns87415.c | 50 +++++++++-------- drivers/ide/pci/scc_pata.c | 29 +++++----- drivers/ide/pci/sgiioc4.c | 18 +++++- drivers/ide/ppc/pmac.c | 21 +++++-- drivers/ide/setup-pci.c | 2 +- drivers/scsi/ide-scsi.c | 7 ++- include/linux/ide.h | 52 ++++++++++++------ 23 files changed, 358 insertions(+), 225 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 0fd01d630f12..0283d162f7f7 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -382,7 +382,7 @@ static void icside_dma_timeout(ide_drive_t *drive) if (icside_dma_test_irq(drive)) return; - ide_dump_status(drive, "DMA timeout", hwif->read_status(hwif)); + ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif)); icside_dma_end(drive); } diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 0795d6554913..84644e150531 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -155,6 +155,21 @@ static void h8300_output_data(ide_drive_t *drive, struct request *rq, mm_outsw(drive->hwif->io_ports.data_addr, buf, (len + 1) / 2); } +static const struct ide_tp_ops h8300_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = h8300_tf_load, + .tf_read = h8300_tf_read, + + .input_data = h8300_input_data, + .output_data = h8300_output_data, +}; + #define H8300_IDE_GAP (2) static inline void hw_setup(hw_regs_t *hw) @@ -169,16 +184,8 @@ static inline void hw_setup(hw_regs_t *hw) hw->chipset = ide_generic; } -static inline void hwif_setup(ide_hwif_t *hwif) -{ - hwif->tf_load = h8300_tf_load; - hwif->tf_read = h8300_tf_read; - - hwif->input_data = h8300_input_data; - hwif->output_data = h8300_output_data; -} - static const struct ide_port_info h8300_port_info = { + .tp_ops = &h8300_tp_ops, .host_flags = IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_DMA, }; @@ -205,7 +212,6 @@ static int __init h8300_ide_init(void) return -ENOENT; index = hwif->index; - hwif_setup(hwif); idx[0] = index; diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index f17a00ccbe96..6789b81ea78d 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -22,6 +22,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int)) { ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; xfer_func_t *xferfunc; unsigned int temp; u16 bcount; @@ -35,7 +36,7 @@ ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc, } /* Clear the interrupt */ - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { if (hwif->dma_ops->dma_end(drive) || @@ -140,7 +141,7 @@ cmd_finished: if (pc->sg) io_buffers(drive, pc, temp, 0); else - hwif->input_data(drive, NULL, + tp_ops->input_data(drive, NULL, pc->cur_pos, temp); printk(KERN_ERR "%s: transferred %d of " "%d bytes\n", @@ -157,9 +158,9 @@ cmd_finished: debug_log("The device wants to send us more data than " "expected - allowing transfer\n"); } - xferfunc = hwif->input_data; + xferfunc = tp_ops->input_data; } else - xferfunc = hwif->output_data; + xferfunc = tp_ops->output_data; if ((drive->media == ide_floppy && !scsi && !pc->buf) || (drive->media == ide_tape && !scsi && pc->bh) || @@ -188,7 +189,7 @@ static u8 ide_read_ireason(ide_drive_t *drive) memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_IN_NSECT; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); return task.tf.nsect & 3; } @@ -249,7 +250,7 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, /* Send the actual packet */ if ((pc->flags & PC_FLAG_ZIP_DRIVE) == 0) - hwif->output_data(drive, NULL, pc->c, 12); + hwif->tp_ops->output_data(drive, NULL, pc->c, 12); return ide_started; } diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 563a380d0e7a..d9798ca433ba 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -285,7 +285,7 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) int stat, err, sense_key; /* check for errors */ - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (stat_ret) *stat_ret = stat; @@ -590,7 +590,7 @@ static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, cmd_len = ATAPI_MIN_CDB_BYTES; /* send the command to the device */ - hwif->output_data(drive, NULL, rq->cmd, cmd_len); + hwif->tp_ops->output_data(drive, NULL, rq->cmd, cmd_len); /* start the DMA if need be */ if (info->dma) @@ -627,7 +627,7 @@ static int ide_cd_check_ireason(ide_drive_t *drive, struct request *rq, * Some drives (ASUS) seem to tell us that status info is * available. Just get it and ignore. */ - (void)hwif->read_status(hwif); + (void)hwif->tp_ops->read_status(hwif); return 0; } else { /* drive wants a command packet, or invalid ireason... */ @@ -990,10 +990,10 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) if (ireason == 0) { write = 1; - xferfunc = hwif->output_data; + xferfunc = hwif->tp_ops->output_data; } else { write = 0; - xferfunc = hwif->input_data; + xferfunc = hwif->tp_ops->input_data; } /* transfer data */ @@ -1200,7 +1200,7 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, if (info->cd_flags & IDE_CD_FLAG_SEEKING) { ide_hwif_t *hwif = drive->hwif; unsigned long elapsed = jiffies - info->start_seek; - int stat = hwif->read_status(hwif); + int stat = hwif->tp_ops->read_status(hwif); if ((stat & SEEK_STAT) != SEEK_STAT) { if (elapsed < IDECD_SEEK_TIMEOUT) { diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index e72112efab9a..be99d463dcc7 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -104,7 +104,7 @@ ide_startstop_t ide_dma_intr (ide_drive_t *drive) u8 stat = 0, dma_stat = 0; dma_stat = hwif->dma_ops->dma_end(drive); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { if (!dma_stat) { @@ -335,7 +335,7 @@ static int config_drive_for_dma (ide_drive_t *drive) static int dma_timer_expiry (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->read_sff_dma_status(hwif); + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); printk(KERN_WARNING "%s: dma_timer_expiry: dma status == 0x%02x\n", drive->name, dma_stat); @@ -370,7 +370,7 @@ void ide_dma_host_set(ide_drive_t *drive, int on) { ide_hwif_t *hwif = HWIF(drive); u8 unit = (drive->select.b.unit & 0x01); - u8 dma_stat = hwif->read_sff_dma_status(hwif); + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); if (on) dma_stat |= (1 << (5 + unit)); @@ -482,7 +482,7 @@ int ide_dma_setup(ide_drive_t *drive) outb(reading, hwif->dma_base + ATA_DMA_CMD); /* read DMA status for INTR & ERROR flags */ - dma_stat = hwif->read_sff_dma_status(hwif); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); /* clear INTR & ERROR flags */ if (mmio) @@ -551,7 +551,7 @@ int __ide_dma_end (ide_drive_t *drive) } /* get DMA status */ - dma_stat = hwif->read_sff_dma_status(hwif); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); if (mmio) /* clear the INTR & ERROR bits */ @@ -574,7 +574,7 @@ EXPORT_SYMBOL(__ide_dma_end); int ide_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - u8 dma_stat = hwif->read_sff_dma_status(hwif); + u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); /* return 1 if INTR asserted */ if ((dma_stat & 4) == 4) diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 6f5294cfff23..62be2b27f236 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -247,9 +247,9 @@ static void ide_floppy_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, data = bvec_kmap_irq(bvec, &flags); if (direction) - hwif->output_data(drive, NULL, data, count); + hwif->tp_ops->output_data(drive, NULL, data, count); else - hwif->input_data(drive, NULL, data, count); + hwif->tp_ops->input_data(drive, NULL, data, count); bvec_kunmap_irq(data, &flags); bcount -= count; @@ -402,7 +402,7 @@ static int idefloppy_transfer_pc(ide_drive_t *drive) idefloppy_floppy_t *floppy = drive->driver_data; /* Send the actual packet */ - drive->hwif->output_data(drive, NULL, floppy->pc->c, 12); + drive->hwif->tp_ops->output_data(drive, NULL, floppy->pc->c, 12); /* Timeout for the packet command */ return IDEFLOPPY_WAIT_CMD; @@ -954,7 +954,7 @@ static int idefloppy_get_format_progress(ide_drive_t *drive, int __user *arg) u8 stat; local_irq_save(flags); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); local_irq_restore(flags); progress_indication = ((stat & SEEK_STAT) == 0) ? 0 : 0x10000; diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index bbd7bd4c48ee..a896a283f27f 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -330,7 +330,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err) tf->error = err; tf->status = stat; - drive->hwif->tf_read(drive, task); + drive->hwif->tp_ops->tf_read(drive, task); if (task->tf_flags & IDE_TFLAG_DYN) kfree(task); @@ -381,7 +381,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 if (err == ABRT_ERR) { if (drive->select.b.lba && /* some newer drives don't support WIN_SPECIFY */ - hwif->read_status(hwif) == WIN_SPECIFY) + hwif->tp_ops->read_status(hwif) == WIN_SPECIFY) return ide_stopped; } else if ((err & BAD_CRC) == BAD_CRC) { /* UDMA crc error, just retry the operation */ @@ -407,7 +407,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 return ide_stopped; } - if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) rq->errors |= ERROR_RESET; if ((rq->errors & ERROR_RESET) == ERROR_RESET) { @@ -434,9 +434,9 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u /* add decoding error stuff */ } - if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); + hwif->tp_ops->exec_command(hwif, WIN_IDLEIMMEDIATE); if (rq->errors >= ERROR_MAX) { ide_kill_rq(drive, rq); @@ -710,7 +710,7 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, #ifdef DEBUG printk("%s: DRIVE_CMD (null)\n", drive->name); #endif - ide_end_drive_cmd(drive, hwif->read_status(hwif), + ide_end_drive_cmd(drive, hwif->tp_ops->read_status(hwif), ide_read_error(drive)); return ide_stopped; @@ -755,7 +755,7 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq) if (rc) printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); SELECT_DRIVE(drive); - hwif->set_irq(hwif, 1); + hwif->tp_ops->set_irq(hwif, 1); rc = ide_wait_not_busy(hwif, 100000); if (rc) printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); @@ -1042,7 +1042,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) * quirk_list may not like intr setups/cleanups */ if (drive->quirk_list != 1) - hwif->set_irq(hwif, 0); + hwif->tp_ops->set_irq(hwif, 0); } hwgroup->hwif = hwif; hwgroup->drive = drive; @@ -1142,7 +1142,7 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); (void)hwif->dma_ops->dma_end(drive); ret = ide_error(drive, "dma timeout error", - hwif->read_status(hwif)); + hwif->tp_ops->read_status(hwif)); } else { printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name); hwif->dma_ops->dma_timeout(drive); @@ -1267,7 +1267,7 @@ void ide_timer_expiry (unsigned long data) } else startstop = ide_error(drive, "irq timeout", - hwif->read_status(hwif)); + hwif->tp_ops->read_status(hwif)); } drive->service_time = jiffies - drive->service_start; spin_lock_irq(&ide_lock); @@ -1323,7 +1323,7 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) */ do { if (hwif->irq == irq) { - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { /* Try to not flood the console with msgs */ @@ -1414,7 +1414,7 @@ irqreturn_t ide_intr (int irq, void *dev_id) * Whack the status register, just in case * we have a leftover pending IRQ. */ - (void)hwif->read_status(hwif); + (void)hwif->tp_ops->read_status(hwif); #endif /* CONFIG_BLK_DEV_IDEPCI */ } spin_unlock_irqrestore(&ide_lock, flags); @@ -1531,9 +1531,9 @@ void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma) task.tf.lbah = (bcount >> 8) & 0xff; ide_tf_dump(drive->name, &task.tf); - hwif->set_irq(hwif, 1); + hwif->tp_ops->set_irq(hwif, 1); SELECT_MASK(drive, 0); - hwif->tf_load(drive, &task); + hwif->tp_ops->tf_load(drive, &task); } EXPORT_SYMBOL_GPL(ide_pktcmd_tf_load); @@ -1545,9 +1545,9 @@ void ide_pad_transfer(ide_drive_t *drive, int write, int len) while (len > 0) { if (write) - hwif->output_data(drive, NULL, buf, min(4, len)); + hwif->tp_ops->output_data(drive, NULL, buf, min(4, len)); else - hwif->input_data(drive, NULL, buf, min(4, len)); + hwif->tp_ops->input_data(drive, NULL, buf, min(4, len)); len -= 4; } } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index c9d15be4c48d..07da5fb9eaff 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -68,7 +68,7 @@ void SELECT_DRIVE (ide_drive_t *drive) memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_OUT_DEVICE; - drive->hwif->tf_load(drive, &task); + drive->hwif->tp_ops->tf_load(drive, &task); } void SELECT_MASK(ide_drive_t *drive, int mask) @@ -79,39 +79,43 @@ void SELECT_MASK(ide_drive_t *drive, int mask) port_ops->maskproc(drive, mask); } -static void ide_exec_command(ide_hwif_t *hwif, u8 cmd) +void ide_exec_command(ide_hwif_t *hwif, u8 cmd) { if (hwif->host_flags & IDE_HFLAG_MMIO) writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); else outb(cmd, hwif->io_ports.command_addr); } +EXPORT_SYMBOL_GPL(ide_exec_command); -static u8 ide_read_status(ide_hwif_t *hwif) +u8 ide_read_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) return readb((void __iomem *)hwif->io_ports.status_addr); else return inb(hwif->io_ports.status_addr); } +EXPORT_SYMBOL_GPL(ide_read_status); -static u8 ide_read_altstatus(ide_hwif_t *hwif) +u8 ide_read_altstatus(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) return readb((void __iomem *)hwif->io_ports.ctl_addr); else return inb(hwif->io_ports.ctl_addr); } +EXPORT_SYMBOL_GPL(ide_read_altstatus); -static u8 ide_read_sff_dma_status(ide_hwif_t *hwif) +u8 ide_read_sff_dma_status(ide_hwif_t *hwif) { if (hwif->host_flags & IDE_HFLAG_MMIO) return readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)); else return inb(hwif->dma_base + ATA_DMA_STATUS); } +EXPORT_SYMBOL_GPL(ide_read_sff_dma_status); -static void ide_set_irq(ide_hwif_t *hwif, int on) +void ide_set_irq(ide_hwif_t *hwif, int on) { u8 ctl = ATA_DEVCTL_OBS; @@ -127,8 +131,9 @@ static void ide_set_irq(ide_hwif_t *hwif, int on) else outb(ctl, hwif->io_ports.ctl_addr); } +EXPORT_SYMBOL_GPL(ide_set_irq); -static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) +void ide_tf_load(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -180,8 +185,9 @@ static void ide_tf_load(ide_drive_t *drive, ide_task_t *task) tf_outb((tf->device & HIHI) | drive->select.all, io_ports->device_addr); } +EXPORT_SYMBOL_GPL(ide_tf_load); -static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) +void ide_tf_read(ide_drive_t *drive, ide_task_t *task) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -241,6 +247,7 @@ static void ide_tf_read(ide_drive_t *drive, ide_task_t *task) tf->hob_lbah = tf_inb(io_ports->lbah_addr); } } +EXPORT_SYMBOL_GPL(ide_tf_read); /* * Some localbus EIDE interfaces require a special access sequence @@ -263,8 +270,8 @@ static void ata_vlb_sync(unsigned long port) * so if an odd len is specified, be sure that there's at least one * extra byte allocated for the buffer. */ -static void ata_input_data(ide_drive_t *drive, struct request *rq, - void *buf, unsigned int len) +void ide_input_data(ide_drive_t *drive, struct request *rq, void *buf, + unsigned int len) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -304,12 +311,13 @@ static void ata_input_data(ide_drive_t *drive, struct request *rq, insw(data_addr, buf, len / 2); } } +EXPORT_SYMBOL_GPL(ide_input_data); /* * This is used for most PIO data transfers *to* the IDE interface */ -static void ata_output_data(ide_drive_t *drive, struct request *rq, - void *buf, unsigned int len) +void ide_output_data(ide_drive_t *drive, struct request *rq, void *buf, + unsigned int len) { ide_hwif_t *hwif = drive->hwif; struct ide_io_ports *io_ports = &hwif->io_ports; @@ -347,22 +355,7 @@ static void ata_output_data(ide_drive_t *drive, struct request *rq, outsw(data_addr, buf, len / 2); } } - -void default_hwif_transport(ide_hwif_t *hwif) -{ - hwif->exec_command = ide_exec_command; - hwif->read_status = ide_read_status; - hwif->read_altstatus = ide_read_altstatus; - hwif->read_sff_dma_status = ide_read_sff_dma_status; - - hwif->set_irq = ide_set_irq; - - hwif->tf_load = ide_tf_load; - hwif->tf_read = ide_tf_read; - - hwif->input_data = ata_input_data; - hwif->output_data = ata_output_data; -} +EXPORT_SYMBOL_GPL(ide_output_data); u8 ide_read_error(ide_drive_t *drive) { @@ -371,7 +364,7 @@ u8 ide_read_error(ide_drive_t *drive) memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_IN_FEATURE; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); return task.tf.error; } @@ -385,13 +378,28 @@ void ide_read_bcount_and_ireason(ide_drive_t *drive, u16 *bcount, u8 *ireason) task.tf_flags = IDE_TFLAG_IN_LBAH | IDE_TFLAG_IN_LBAM | IDE_TFLAG_IN_NSECT; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); *bcount = (task.tf.lbah << 8) | task.tf.lbam; *ireason = task.tf.nsect & 3; } EXPORT_SYMBOL_GPL(ide_read_bcount_and_ireason); +const struct ide_tp_ops default_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + void ide_fix_driveid (struct hd_driveid *id) { #ifndef __LITTLE_ENDIAN @@ -545,10 +553,10 @@ int drive_is_ready (ide_drive_t *drive) * about possible isa-pnp and pci-pnp issues yet. */ if (hwif->io_ports.ctl_addr) - stat = hwif->read_altstatus(hwif); + stat = hwif->tp_ops->read_altstatus(hwif); else /* Note: this may clear a pending IRQ!! */ - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (stat & BUSY_STAT) /* drive busy: definitely not interrupting */ @@ -574,24 +582,25 @@ EXPORT_SYMBOL(drive_is_ready); static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long timeout, u8 *rstat) { ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; unsigned long flags; int i; u8 stat; udelay(1); /* spec allows drive 400ns to assert "BUSY" */ - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (stat & BUSY_STAT) { local_irq_set(flags); timeout += jiffies; - while ((stat = hwif->read_status(hwif)) & BUSY_STAT) { + while ((stat = tp_ops->read_status(hwif)) & BUSY_STAT) { if (time_after(jiffies, timeout)) { /* * One last read after the timeout in case * heavy interrupt load made us not make any * progress during the timeout.. */ - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (!(stat & BUSY_STAT)) break; @@ -611,7 +620,7 @@ static int __ide_wait_stat(ide_drive_t *drive, u8 good, u8 bad, unsigned long ti */ for (i = 0; i < 10; i++) { udelay(1); - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (OK_STAT(stat, good, bad)) { *rstat = stat; @@ -737,6 +746,7 @@ no_80w: int ide_driveid_update(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; struct hd_driveid *id; unsigned long timeout, flags; u8 stat; @@ -747,9 +757,9 @@ int ide_driveid_update(ide_drive_t *drive) */ SELECT_MASK(drive, 1); - hwif->set_irq(hwif, 0); + tp_ops->set_irq(hwif, 0); msleep(50); - hwif->exec_command(hwif, WIN_IDENTIFY); + tp_ops->exec_command(hwif, WIN_IDENTIFY); timeout = jiffies + WAIT_WORSTCASE; do { if (time_after(jiffies, timeout)) { @@ -758,11 +768,11 @@ int ide_driveid_update(ide_drive_t *drive) } msleep(50); /* give drive a breather */ - stat = hwif->read_altstatus(hwif); + stat = tp_ops->read_altstatus(hwif); } while (stat & BUSY_STAT); msleep(50); /* wait for IRQ and DRQ_STAT */ - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (!OK_STAT(stat, DRQ_STAT, BAD_R_STAT)) { SELECT_MASK(drive, 0); @@ -776,8 +786,8 @@ int ide_driveid_update(ide_drive_t *drive) local_irq_restore(flags); return 0; } - hwif->input_data(drive, NULL, id, SECTOR_SIZE); - (void)hwif->read_status(hwif); /* clear drive IRQ */ + tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); + (void)tp_ops->read_status(hwif); /* clear drive IRQ */ local_irq_enable(); local_irq_restore(flags); ide_fix_driveid(id); @@ -798,6 +808,7 @@ int ide_driveid_update(ide_drive_t *drive) int ide_config_drive_speed(ide_drive_t *drive, u8 speed) { ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int error = 0; u8 stat; ide_task_t task; @@ -833,19 +844,19 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed) SELECT_DRIVE(drive); SELECT_MASK(drive, 0); udelay(1); - hwif->set_irq(hwif, 0); + tp_ops->set_irq(hwif, 0); memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_OUT_FEATURE | IDE_TFLAG_OUT_NSECT; task.tf.feature = SETFEATURES_XFER; task.tf.nsect = speed; - hwif->tf_load(drive, &task); + tp_ops->tf_load(drive, &task); - hwif->exec_command(hwif, WIN_SETFEATURES); + tp_ops->exec_command(hwif, WIN_SETFEATURES); if (drive->quirk_list == 2) - hwif->set_irq(hwif, 1); + tp_ops->set_irq(hwif, 1); error = __ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT|ERR_STAT, @@ -950,7 +961,7 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler, spin_lock_irqsave(&ide_lock, flags); __ide_set_handler(drive, handler, timeout, expiry); - hwif->exec_command(hwif, cmd); + hwif->tp_ops->exec_command(hwif, cmd); /* * Drive takes 400nS to respond, we must avoid the IRQ being * serviced before that. @@ -968,7 +979,7 @@ void ide_execute_pkt_cmd(ide_drive_t *drive) unsigned long flags; spin_lock_irqsave(&ide_lock, flags); - hwif->exec_command(hwif, WIN_PACKETCMD); + hwif->tp_ops->exec_command(hwif, WIN_PACKETCMD); ndelay(400); spin_unlock_irqrestore(&ide_lock, flags); } @@ -999,7 +1010,7 @@ static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) SELECT_DRIVE(drive); udelay (10); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat, 0, BUSY_STAT)) printk("%s: ATAPI reset complete\n", drive->name); @@ -1045,7 +1056,7 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive) } } - tmp = hwif->read_status(hwif); + tmp = hwif->tp_ops->read_status(hwif); if (!OK_STAT(tmp, 0, BUSY_STAT)) { if (time_before(jiffies, hwgroup->poll_timeout)) { @@ -1159,6 +1170,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) ide_hwif_t *hwif; ide_hwgroup_t *hwgroup; struct ide_io_ports *io_ports; + const struct ide_tp_ops *tp_ops; const struct ide_port_ops *port_ops; spin_lock_irqsave(&ide_lock, flags); @@ -1167,6 +1179,8 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) io_ports = &hwif->io_ports; + tp_ops = hwif->tp_ops; + /* We must not reset with running handlers */ BUG_ON(hwgroup->handler != NULL); @@ -1175,7 +1189,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) pre_reset(drive); SELECT_DRIVE(drive); udelay (20); - hwif->exec_command(hwif, WIN_SRST); + tp_ops->exec_command(hwif, WIN_SRST); ndelay(400); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; hwgroup->polling = 1; @@ -1208,11 +1222,11 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) * TODO: add ->softreset method and stop abusing ->set_irq */ /* set SRST and nIEN */ - hwif->set_irq(hwif, 4); + tp_ops->set_irq(hwif, 4); /* more than enough time */ udelay(10); /* clear SRST, leave nIEN (unless device is on the quirk list) */ - hwif->set_irq(hwif, drive->quirk_list == 2); + tp_ops->set_irq(hwif, drive->quirk_list == 2); /* more than enough time */ udelay(10); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; @@ -1257,7 +1271,7 @@ int ide_wait_not_busy(ide_hwif_t *hwif, unsigned long timeout) * about locking issues (2.5 work ?). */ mdelay(1); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if ((stat & BUSY_STAT) == 0) return 0; /* diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 7ac44d515470..97fefabea8b8 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -325,7 +325,7 @@ static void ide_dump_sector(ide_drive_t *drive) else task.tf_flags = IDE_TFLAG_IN_LBA | IDE_TFLAG_IN_DEVICE; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); if (lba48 || (tf->device & ATA_LBA)) printk(", LBAsect=%llu", diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 3cc8ade2cc4f..c588066295db 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -126,7 +126,7 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd) id = drive->id; /* read 512 bytes of id info */ - hwif->input_data(drive, NULL, id, SECTOR_SIZE); + hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); drive->id_read = 1; local_irq_enable(); @@ -267,6 +267,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) { ide_hwif_t *hwif = HWIF(drive); struct ide_io_ports *io_ports = &hwif->io_ports; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int use_altstatus = 0, rc; unsigned long timeout; u8 s = 0, a = 0; @@ -275,8 +276,8 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) msleep(50); if (io_ports->ctl_addr) { - a = hwif->read_altstatus(hwif); - s = hwif->read_status(hwif); + a = tp_ops->read_altstatus(hwif); + s = tp_ops->read_status(hwif); if ((a ^ s) & ~INDEX_STAT) /* ancient Seagate drives, broken interfaces */ printk(KERN_INFO "%s: probing with STATUS(0x%02x) " @@ -297,11 +298,11 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) /* disable DMA & overlap */ task.tf_flags = IDE_TFLAG_OUT_FEATURE; - drive->hwif->tf_load(drive, &task); + tp_ops->tf_load(drive, &task); } /* ask drive for ID */ - hwif->exec_command(hwif, cmd); + tp_ops->exec_command(hwif, cmd); timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; timeout += jiffies; @@ -312,13 +313,13 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) } /* give drive a breather */ msleep(50); - s = use_altstatus ? hwif->read_altstatus(hwif) - : hwif->read_status(hwif); + s = use_altstatus ? tp_ops->read_altstatus(hwif) + : tp_ops->read_status(hwif); } while (s & BUSY_STAT); /* wait for IRQ and DRQ_STAT */ msleep(50); - s = hwif->read_status(hwif); + s = tp_ops->read_status(hwif); if (OK_STAT(s, DRQ_STAT, BAD_R_STAT)) { unsigned long flags; @@ -330,7 +331,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) /* drive responded with ID */ rc = 0; /* clear drive IRQ */ - (void)hwif->read_status(hwif); + (void)tp_ops->read_status(hwif); local_irq_restore(flags); } else { /* drive refused ID */ @@ -352,6 +353,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd) static int try_to_identify (ide_drive_t *drive, u8 cmd) { ide_hwif_t *hwif = HWIF(drive); + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int retval; int autoprobe = 0; unsigned long cookie = 0; @@ -367,7 +369,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) autoprobe = 1; cookie = probe_irq_on(); } - hwif->set_irq(hwif, autoprobe); + tp_ops->set_irq(hwif, autoprobe); } retval = actual_try_to_identify(drive, cmd); @@ -375,9 +377,9 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd) if (autoprobe) { int irq; - hwif->set_irq(hwif, 0); + tp_ops->set_irq(hwif, 0); /* clear drive IRQ */ - (void)hwif->read_status(hwif); + (void)tp_ops->read_status(hwif); udelay(5); irq = probe_irq_off(cookie); if (!hwif->irq) { @@ -402,7 +404,7 @@ static int ide_busy_sleep(ide_hwif_t *hwif) do { msleep(50); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if ((stat & BUSY_STAT) == 0) return 0; } while (time_before(jiffies, timeout)); @@ -417,7 +419,7 @@ static u8 ide_read_device(ide_drive_t *drive) memset(&task, 0, sizeof(task)); task.tf_flags = IDE_TFLAG_IN_DEVICE; - drive->hwif->tf_read(drive, &task); + drive->hwif->tp_ops->tf_read(drive, &task); return task.tf.device; } @@ -446,6 +448,7 @@ static u8 ide_read_device(ide_drive_t *drive) static int do_probe (ide_drive_t *drive, u8 cmd) { ide_hwif_t *hwif = HWIF(drive); + const struct ide_tp_ops *tp_ops = hwif->tp_ops; int rc; u8 stat; @@ -478,7 +481,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) return 3; } - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (OK_STAT(stat, READY_STAT, BUSY_STAT) || drive->present || cmd == WIN_PIDENTIFY) { @@ -488,7 +491,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) rc = try_to_identify(drive,cmd); } - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (stat == (BUSY_STAT | READY_STAT)) return 4; @@ -499,13 +502,13 @@ static int do_probe (ide_drive_t *drive, u8 cmd) msleep(50); SELECT_DRIVE(drive); msleep(50); - hwif->exec_command(hwif, WIN_SRST); + tp_ops->exec_command(hwif, WIN_SRST); (void)ide_busy_sleep(hwif); rc = try_to_identify(drive, cmd); } /* ensure drive IRQ is clear */ - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (rc == 1) printk(KERN_ERR "%s: no response (status = 0x%02x)\n", @@ -519,7 +522,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd) SELECT_DRIVE(&hwif->drives[0]); msleep(50); /* ensure drive irq is clear */ - (void)hwif->read_status(hwif); + (void)tp_ops->read_status(hwif); } return rc; } @@ -530,12 +533,13 @@ static int do_probe (ide_drive_t *drive, u8 cmd) static void enable_nest (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); + const struct ide_tp_ops *tp_ops = hwif->tp_ops; u8 stat; printk("%s: enabling %s -- ", hwif->name, drive->id->model); SELECT_DRIVE(drive); msleep(50); - hwif->exec_command(hwif, EXABYTE_ENABLE_NEST); + tp_ops->exec_command(hwif, EXABYTE_ENABLE_NEST); if (ide_busy_sleep(hwif)) { printk(KERN_CONT "failed (timeout)\n"); @@ -544,7 +548,7 @@ static void enable_nest (ide_drive_t *drive) msleep(50); - stat = hwif->read_status(hwif); + stat = tp_ops->read_status(hwif); if (!OK_STAT(stat, 0, BAD_STAT)) printk(KERN_CONT "failed (status = 0x%02x)\n", stat); @@ -726,7 +730,7 @@ static int ide_port_wait_ready(ide_hwif_t *hwif) /* Ignore disks that we will not probe for later. */ if (!drive->noprobe || drive->present) { SELECT_DRIVE(drive); - hwif->set_irq(hwif, 1); + hwif->tp_ops->set_irq(hwif, 1); mdelay(2); rc = ide_wait_not_busy(hwif, 35000); if (rc) @@ -1083,7 +1087,7 @@ static int init_irq (ide_hwif_t *hwif) sa = IRQF_SHARED; if (io_ports->ctl_addr) - hwif->set_irq(hwif, 1); + hwif->tp_ops->set_irq(hwif, 1); if (request_irq(hwif->irq,&ide_intr,sa,hwif->name,hwgroup)) goto out_unlink; @@ -1361,6 +1365,9 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port, hwif->host_flags |= d->host_flags; hwif->pio_mask = d->pio_mask; + if (d->tp_ops) + hwif->tp_ops = d->tp_ops; + /* ->set_pio_mode for DTC2278 is currently limited to port 0 */ if (hwif->chipset != ide_dtc2278 || hwif->channel == 0) hwif->port_ops = d->port_ops; diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index ef54728a74b0..0af128826f1e 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -398,7 +398,7 @@ static void idetape_input_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, count = min( (unsigned int)(bh->b_size - atomic_read(&bh->b_count)), bcount); - drive->hwif->input_data(drive, NULL, bh->b_data + + drive->hwif->tp_ops->input_data(drive, NULL, bh->b_data + atomic_read(&bh->b_count), count); bcount -= count; atomic_add(count, &bh->b_count); @@ -424,7 +424,7 @@ static void idetape_output_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, return; } count = min((unsigned int)pc->b_count, (unsigned int)bcount); - drive->hwif->output_data(drive, NULL, pc->b_data, count); + drive->hwif->tp_ops->output_data(drive, NULL, pc->b_data, count); bcount -= count; pc->b_data += count; pc->b_count -= count; @@ -932,7 +932,7 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) struct ide_atapi_pc *pc = tape->pc; u8 stat; - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (stat & SEEK_STAT) { if (stat & ERR_STAT) { @@ -1019,7 +1019,7 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive, * If the tape is still busy, postpone our request and service * the other device meanwhile. */ - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (!drive->dsc_overlap && !(rq->cmd[0] & REQ_IDETAPE_PC2)) set_bit(IDETAPE_FLAG_IGNORE_DSC, &tape->flags); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index ea345369553e..aeddbbd69e86 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -64,6 +64,7 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) ide_hwif_t *hwif = HWIF(drive); struct ide_taskfile *tf = &task->tf; ide_handler_t *handler = NULL; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; const struct ide_dma_ops *dma_ops = hwif->dma_ops; if (task->data_phase == TASKFILE_MULTI_IN || @@ -80,15 +81,15 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { ide_tf_dump(drive->name, tf); - hwif->set_irq(hwif, 1); + tp_ops->set_irq(hwif, 1); SELECT_MASK(drive, 0); - hwif->tf_load(drive, task); + tp_ops->tf_load(drive, task); } switch (task->data_phase) { case TASKFILE_MULTI_OUT: case TASKFILE_OUT: - hwif->exec_command(hwif, tf->command); + tp_ops->exec_command(hwif, tf->command); ndelay(400); /* FIXME */ return pre_task_out_intr(drive, task->rq); case TASKFILE_MULTI_IN: @@ -125,7 +126,7 @@ EXPORT_SYMBOL_GPL(do_rw_taskfile); static ide_startstop_t set_multmode_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - u8 stat = hwif->read_status(hwif); + u8 stat = hwif->tp_ops->read_status(hwif); if (OK_STAT(stat, READY_STAT, BAD_STAT)) drive->mult_count = drive->mult_req; @@ -146,8 +147,12 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) int retries = 5; u8 stat; - while (((stat = hwif->read_status(hwif)) & BUSY_STAT) && retries--) + while (1) { + stat = hwif->tp_ops->read_status(hwif); + if ((stat & BUSY_STAT) == 0 || retries-- == 0) + break; udelay(10); + }; if (OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_stopped; @@ -165,7 +170,7 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive) static ide_startstop_t recal_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; - u8 stat = hwif->read_status(hwif); + u8 stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "recal_intr", stat); @@ -182,7 +187,7 @@ static ide_startstop_t task_no_data_intr(ide_drive_t *drive) u8 stat; local_irq_enable_in_hardirq(); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) return ide_error(drive, "task_no_data_intr", stat); @@ -205,7 +210,7 @@ static u8 wait_drive_not_busy(ide_drive_t *drive) * take up to 6 ms on some ATAPI devices, so we will wait max 10 ms. */ for (retries = 0; retries < 1000; retries++) { - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (stat & BUSY_STAT) udelay(10); @@ -260,9 +265,9 @@ static void ide_pio_sector(ide_drive_t *drive, struct request *rq, /* do the actual data transfer */ if (write) - hwif->output_data(drive, rq, buf, SECTOR_SIZE); + hwif->tp_ops->output_data(drive, rq, buf, SECTOR_SIZE); else - hwif->input_data(drive, rq, buf, SECTOR_SIZE); + hwif->tp_ops->input_data(drive, rq, buf, SECTOR_SIZE); kunmap_atomic(buf, KM_BIO_SRC_IRQ); #ifdef CONFIG_HIGHMEM @@ -389,7 +394,7 @@ static ide_startstop_t task_in_intr(ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; struct request *rq = hwif->hwgroup->rq; - u8 stat = hwif->read_status(hwif); + u8 stat = hwif->tp_ops->read_status(hwif); /* Error? */ if (stat & ERR_STAT) @@ -423,7 +428,7 @@ static ide_startstop_t task_out_intr (ide_drive_t *drive) { ide_hwif_t *hwif = drive->hwif; struct request *rq = HWGROUP(drive)->rq; - u8 stat = hwif->read_status(hwif); + u8 stat = hwif->tp_ops->read_status(hwif); if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) return task_error(drive, rq, __func__, stat); diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 961f31c648c9..132b504168e9 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -101,7 +101,7 @@ void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) init_completion(&hwif->gendev_rel_comp); - default_hwif_transport(hwif); + hwif->tp_ops = &default_tp_ops; ide_port_init_devices_data(hwif); } diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 1bb2aa72cc7f..3e2c6125f031 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -66,6 +66,27 @@ static void falconide_output_data(ide_drive_t *drive, struct request *rq, outsw_swapw(data_addr, buf, (len + 1) / 2); } +/* Atari has a byte-swapped IDE interface */ +static const struct ide_tp_ops falconide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = falconide_input_data, + .output_data = falconide_output_data, +}; + +static const struct ide_port_info falconide_port_info = { + .tp_ops = &falconide_tp_ops, + .host_flags = IDE_HFLAG_NO_DMA, +}; + static void __init falconide_setup_ports(hw_regs_t *hw) { int i; @@ -111,12 +132,8 @@ static int __init falconide_init(void) u8 index = hwif->index; u8 idx[4] = { index, 0xff, 0xff, 0xff }; - /* Atari has a byte-swapped IDE interface */ - hwif->input_data = falconide_input_data; - hwif->output_data = falconide_output_data; - ide_get_lock(NULL, NULL); - ide_device_add(idx, NULL, hws); + ide_device_add(idx, &falconide_port_info, hws); ide_release_lock(); } diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index fcb04b8b0238..2dc306f852a6 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -96,6 +96,27 @@ static void q40ide_output_data(ide_drive_t *drive, struct request *rq, outsw_swapw(data_addr, buf, (len + 1) / 2); } +/* Q40 has a byte-swapped IDE interface */ +static const struct ide_tp_ops q40ide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = q40ide_input_data, + .output_data = q40ide_output_data, +}; + +static const struct ide_port_info q40ide_port_info = { + .tp_ops = &q40ide_tp_ops, + .host_flags = IDE_HFLAG_NO_DMA, +}; + /* * the static array is needed to have the name reported in /proc/ioports, * hwif->name unfortunately isn't available yet @@ -141,16 +162,12 @@ static int __init q40ide_init(void) if (hwif) { hwif->chipset = ide_generic; - /* Q40 has a byte-swapped IDE interface */ - hwif->input_data = q40ide_input_data; - hwif->output_data = q40ide_output_data; - hws[i] = &hw[i]; idx[i] = hwif->index; } } - ide_device_add(idx, NULL, hws); + ide_device_add(idx, &q40ide_port_info, hws); return 0; } diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index 475da582fd89..ed1c9a134079 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -519,6 +519,23 @@ static void auide_setup_ports(hw_regs_t *hw, _auide_hwif *ahwif) *ata_regs = ahwif->regbase + (14 << IDE_REG_SHIFT); } +#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA +static const struct ide_tp_ops au1xxx_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = au1xxx_input_data, + .output_data = au1xxx_output_data, +}; +#endif + static const struct ide_port_ops au1xxx_port_ops = { .set_pio_mode = au1xxx_set_pio_mode, .set_dma_mode = auide_set_dma_mode, @@ -526,6 +543,9 @@ static const struct ide_port_ops au1xxx_port_ops = { static const struct ide_port_info au1xxx_port_info = { .init_dma = auide_ddma_init, +#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA + .tp_ops = &au1xxx_tp_ops, +#endif .port_ops = &au1xxx_port_ops, #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA .dma_ops = &au1xxx_dma_ops, @@ -596,15 +616,6 @@ static int au_ide_probe(struct device *dev) hw.dev = dev; hw.chipset = ide_au1xxx; - /* If the user has selected DDMA assisted copies, - then set up a few local I/O function entry points - */ - -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA - hwif->input_data = au1xxx_input_data; - hwif->output_data = au1xxx_output_data; -#endif - auide_hwif.hwif = hwif; idx[0] = hwif->index; diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 02fd3a877afa..5cd2b32ff0ef 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -104,7 +104,22 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task) } } -static void __devinit superio_ide_init_iops (struct hwif_s *hwif) +static const struct ide_tp_ops superio_tp_ops = { + .exec_command = ide_exec_command, + .read_status = superio_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = superio_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = superio_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + +static void __devinit superio_init_iops(struct hwif_s *hwif) { struct pci_dev *pdev = to_pci_dev(hwif->dev); u32 dma_stat; @@ -115,21 +130,6 @@ static void __devinit superio_ide_init_iops (struct hwif_s *hwif) /* Clear error/interrupt, enable dma */ tmp = superio_ide_inb(dma_stat); outb(tmp | 0x66, dma_stat); - - hwif->read_status = superio_read_status; - hwif->read_sff_dma_status = superio_read_sff_dma_status; - - hwif->tf_read = superio_tf_read; - -} - -static void __devinit init_iops_ns87415(ide_hwif_t *hwif) -{ - struct pci_dev *dev = to_pci_dev(hwif->dev); - - if (PCI_SLOT(dev->devfn) == 0xE) - /* Built-in - assume it's under superio. */ - superio_ide_init_iops(hwif); } #endif @@ -195,7 +195,7 @@ static int ns87415_dma_end(ide_drive_t *drive) u8 dma_stat = 0, dma_cmd = 0; drive->waiting_for_dma = 0; - dma_stat = hwif->read_sff_dma_status(hwif); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); /* get DMA command mode */ dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); /* stop DMA */ @@ -271,7 +271,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) outb(8, hwif->io_ports.ctl_addr); do { udelay(50); - stat = hwif->read_status(hwif); + stat = hwif->tp_ops->read_status(hwif); if (stat == 0xff) break; } while ((stat & BUSY_STAT) && --timeout); @@ -306,9 +306,6 @@ static const struct ide_dma_ops ns87415_dma_ops = { static const struct ide_port_info ns87415_chipset __devinitdata = { .name = "NS87415", -#ifdef CONFIG_SUPERIO - .init_iops = init_iops_ns87415, -#endif .init_hwif = init_hwif_ns87415, .port_ops = &ns87415_port_ops, .dma_ops = &ns87415_dma_ops, @@ -318,7 +315,16 @@ static const struct ide_port_info ns87415_chipset __devinitdata = { static int __devinit ns87415_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &ns87415_chipset); + struct ide_port_info d = ns87415_chipset; + +#ifdef CONFIG_SUPERIO + if (PCI_SLOT(dev->devfn) == 0xE) { + /* Built-in - assume it's under superio. */ + d.init_iops = superio_init_iops; + d.tp_ops = &superio_tp_ops; + } +#endif + return ide_setup_pci_device(dev, &d); } static const struct pci_device_id ns87415_pci_tbl[] = { diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 38765d9b0314..5b1a0e950dfd 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -808,19 +808,6 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) ide_set_hwifdata(hwif, ports); - hwif->exec_command = scc_exec_command; - hwif->read_status = scc_read_status; - hwif->read_altstatus = scc_read_altstatus; - hwif->read_sff_dma_status = scc_read_sff_dma_status; - - hwif->set_irq = scc_set_irq; - - hwif->tf_load = scc_tf_load; - hwif->tf_read = scc_tf_read; - - hwif->input_data = scc_input_data; - hwif->output_data = scc_output_data; - hwif->dma_base = dma_base; hwif->config_data = ports->ctl; } @@ -872,6 +859,21 @@ static void __devinit init_hwif_scc(ide_hwif_t *hwif) hwif->ultra_mask = ATA_UDMA5; /* 100MHz */ } +static const struct ide_tp_ops scc_tp_ops = { + .exec_command = scc_exec_command, + .read_status = scc_read_status, + .read_altstatus = scc_read_altstatus, + .read_sff_dma_status = scc_read_sff_dma_status, + + .set_irq = scc_set_irq, + + .tf_load = scc_tf_load, + .tf_read = scc_tf_read, + + .input_data = scc_input_data, + .output_data = scc_output_data, +}; + static const struct ide_port_ops scc_port_ops = { .set_pio_mode = scc_set_pio_mode, .set_dma_mode = scc_set_dma_mode, @@ -895,6 +897,7 @@ static const struct ide_dma_ops scc_dma_ops = { .name = name_str, \ .init_iops = init_iops_scc, \ .init_hwif = init_hwif_scc, \ + .tp_ops = &scc_tp_ops, \ .port_ops = &scc_port_ops, \ .dma_ops = &scc_dma_ops, \ .host_flags = IDE_HFLAG_SINGLE, \ diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 86f7c4901837..5598bd5936d9 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -550,6 +550,21 @@ static int sgiioc4_dma_setup(ide_drive_t *drive) return 0; } +static const struct ide_tp_ops sgiioc4_tp_ops = { + .exec_command = ide_exec_command, + .read_status = sgiioc4_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = ide_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + static const struct ide_port_ops sgiioc4_port_ops = { .set_dma_mode = sgiioc4_set_dma_mode, /* reset DMA engine, clear IRQs */ @@ -572,6 +587,7 @@ static const struct ide_port_info sgiioc4_port_info __devinitdata = { .name = DRV_NAME, .chipset = ide_pci, .init_dma = ide_dma_sgiioc4, + .tp_ops = &sgiioc4_tp_ops, .port_ops = &sgiioc4_port_ops, .dma_ops = &sgiioc4_dma_ops, .host_flags = IDE_HFLAG_MMIO, @@ -626,8 +642,6 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) /* Initializing chipset IRQ Registers */ writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); - hwif->read_status = sgiioc4_read_status; - idx[0] = hwif->index; if (ide_device_add(idx, &d, hws)) diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index ed073c6635a8..ee557d10a764 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -974,6 +974,21 @@ static void pmac_ide_init_dev(ide_drive_t *drive) } } +static const struct ide_tp_ops pmac_tp_ops = { + .exec_command = pmac_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .read_sff_dma_status = ide_read_sff_dma_status, + + .set_irq = pmac_set_irq, + + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + static const struct ide_port_ops pmac_ide_ata6_port_ops = { .init_dev = pmac_ide_init_dev, .set_pio_mode = pmac_ide_set_pio_mode, @@ -1003,10 +1018,11 @@ static const struct ide_port_info pmac_port_info = { .name = DRV_NAME, .init_dma = pmac_ide_init_dma, .chipset = ide_pmac, + .tp_ops = &pmac_tp_ops, + .port_ops = &pmac_ide_port_ops, #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC .dma_ops = &pmac_dma_ops, #endif - .port_ops = &pmac_ide_port_ops, .host_flags = IDE_HFLAG_SET_PIO_MODE_KEEP_DMA | IDE_HFLAG_POST_SET_MODE | IDE_HFLAG_MMIO | @@ -1106,9 +1122,6 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) if (hwif == NULL) return -ENOENT; - hwif->exec_command = pmac_exec_command; - hwif->set_irq = pmac_set_irq; - idx[0] = hwif->index; ide_device_add(idx, &d, hws); diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index c1b609d9cb28..804c3ef245f9 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -125,7 +125,7 @@ int ide_pci_check_simplex(ide_hwif_t *hwif, const struct ide_port_info *d) * we tune the drive then try to grab DMA ownership if we want to be * the DMA end. This has to be become dynamic to handle hot-plug. */ - dma_stat = hwif->read_sff_dma_status(hwif); + dma_stat = hwif->tp_ops->read_sff_dma_status(hwif); if ((dma_stat & 0x80) && hwif->mate && hwif->mate->dma_base) { printk(KERN_INFO "%s: simplex device: DMA disabled\n", d->name); return -1; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 2a86af91f64a..659db3f7ae08 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -142,7 +142,8 @@ static void ide_scsi_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc, unsigned int bcount, int write) { ide_hwif_t *hwif = drive->hwif; - xfer_func_t *xf = write ? hwif->output_data : hwif->input_data; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + xfer_func_t *xf = write ? tp_ops->output_data : tp_ops->input_data; char *buf; int count; @@ -246,9 +247,9 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err) { ide_hwif_t *hwif = drive->hwif; - if (hwif->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) + if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT)) /* force an abort */ - hwif->exec_command(hwif, WIN_IDLEIMMEDIATE); + hwif->tp_ops->exec_command(hwif, WIN_IDLEIMMEDIATE); rq->errors++; diff --git a/include/linux/ide.h b/include/linux/ide.h index e340218b2a5f..1286a2275efb 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -408,8 +408,28 @@ typedef struct ide_drive_s { ((1<> (c)) & 1) +struct ide_task_s; struct ide_port_info; +struct ide_tp_ops { + void (*exec_command)(struct hwif_s *, u8); + u8 (*read_status)(struct hwif_s *); + u8 (*read_altstatus)(struct hwif_s *); + u8 (*read_sff_dma_status)(struct hwif_s *); + + void (*set_irq)(struct hwif_s *, int); + + void (*tf_load)(ide_drive_t *, struct ide_task_s *); + void (*tf_read)(ide_drive_t *, struct ide_task_s *); + + void (*input_data)(ide_drive_t *, struct request *, void *, + unsigned int); + void (*output_data)(ide_drive_t *, struct request *, void *, + unsigned int); +}; + +extern const struct ide_tp_ops default_tp_ops; + struct ide_port_ops { /* host specific initialization of a device */ void (*init_dev)(ide_drive_t *); @@ -447,8 +467,6 @@ struct ide_dma_ops { void (*dma_timeout)(struct ide_drive_s *); }; -struct ide_task_s; - typedef struct hwif_s { struct hwif_s *next; /* for linked-list in ide_hwgroup_t */ struct hwif_s *mate; /* other hwif from same PCI chip */ @@ -486,22 +504,10 @@ typedef struct hwif_s { void (*rw_disk)(ide_drive_t *, struct request *); + const struct ide_tp_ops *tp_ops; const struct ide_port_ops *port_ops; const struct ide_dma_ops *dma_ops; - void (*exec_command)(struct hwif_s *, u8); - u8 (*read_status)(struct hwif_s *); - u8 (*read_altstatus)(struct hwif_s *); - u8 (*read_sff_dma_status)(struct hwif_s *); - - void (*set_irq)(struct hwif_s *, int); - - void (*tf_load)(ide_drive_t *, struct ide_task_s *); - void (*tf_read)(ide_drive_t *, struct ide_task_s *); - - void (*input_data)(ide_drive_t *, struct request *, void *, unsigned); - void (*output_data)(ide_drive_t *, struct request *, void *, unsigned); - void (*ide_dma_clear_irq)(ide_drive_t *drive); /* dma physical region descriptor table (cpu view) */ @@ -949,6 +955,19 @@ typedef struct ide_task_s { void ide_tf_dump(const char *, struct ide_taskfile *); +void ide_exec_command(ide_hwif_t *, u8); +u8 ide_read_status(ide_hwif_t *); +u8 ide_read_altstatus(ide_hwif_t *); +u8 ide_read_sff_dma_status(ide_hwif_t *); + +void ide_set_irq(ide_hwif_t *, int); + +void ide_tf_load(ide_drive_t *, ide_task_t *); +void ide_tf_read(ide_drive_t *, ide_task_t *); + +void ide_input_data(ide_drive_t *, struct request *, void *, unsigned int); +void ide_output_data(ide_drive_t *, struct request *, void *, unsigned int); + extern void SELECT_DRIVE(ide_drive_t *); void SELECT_MASK(ide_drive_t *, int); @@ -1022,8 +1041,6 @@ static inline int ide_hwif_setup_dma(ide_hwif_t *hwif, } #endif -extern void default_hwif_transport(ide_hwif_t *); - typedef struct ide_pci_enablebit_s { u8 reg; /* byte pci reg holding the enable-bit */ u8 mask; /* mask to isolate the enable-bit */ @@ -1112,6 +1129,7 @@ struct ide_port_info { int (*init_dma)(ide_hwif_t *, const struct ide_port_info *); + const struct ide_tp_ops *tp_ops; const struct ide_port_ops *port_ops; const struct ide_dma_ops *dma_ops; -- cgit From 48c3c1072651922ed153bcf0a33ea82cf20df390 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:57 +0200 Subject: ide: add struct ide_host (take 3) * Add struct ide_host which keeps pointers to host's ports. * Add ide_host_alloc[_all]() and ide_host_remove() helpers. * Pass 'struct ide_host *host' instead of 'u8 *idx' to ide_device_add[_all]() and rename it to ide_host_register[_all](). * Convert host drivers and core code to use struct ide_host. * Remove no longer needed ide_find_port(). * Make ide_find_port_slot() static. * Unexport ide_unregister(). v2: * Add missing 'struct ide_host *host' to macide.c. v3: * Fix build problem in pmac.c (s/ide_alloc_host/ide_host_alloc/) (Noticed by Stephen Rothwell). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 40 +++++------------ drivers/ide/arm/ide_arm.c | 12 ++--- drivers/ide/arm/palm_bk3710.c | 13 ++---- drivers/ide/arm/rapide.c | 17 +++---- drivers/ide/h8300/ide-h8300.c | 14 ++---- drivers/ide/ide-generic.c | 28 ++++-------- drivers/ide/ide-pnp.c | 17 +++---- drivers/ide/ide-probe.c | 94 ++++++++++++++++++++++++++++----------- drivers/ide/ide.c | 2 - drivers/ide/legacy/buddha.c | 19 +++----- drivers/ide/legacy/falconide.c | 11 ++--- drivers/ide/legacy/gayle.c | 16 +++---- drivers/ide/legacy/ide-4drives.c | 23 +++------- drivers/ide/legacy/ide-cs.c | 46 +++++++++---------- drivers/ide/legacy/ide_platform.c | 23 +++++----- drivers/ide/legacy/macide.c | 12 ++--- drivers/ide/legacy/q40ide.c | 15 +++---- drivers/ide/mips/au1xxx-ide.c | 25 +++++------ drivers/ide/mips/swarm.c | 13 +++--- drivers/ide/pci/cmd640.c | 30 ++++--------- drivers/ide/pci/cs5520.c | 8 ++-- drivers/ide/pci/delkin_cb.c | 19 +++----- drivers/ide/pci/scc_pata.c | 24 +++++----- drivers/ide/pci/sgiioc4.c | 13 +++--- drivers/ide/ppc/pmac.c | 10 ++--- drivers/ide/setup-pci.c | 48 ++++++++------------ include/linux/ide.h | 21 ++++----- 27 files changed, 264 insertions(+), 349 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 0283d162f7f7..6fa58425466a 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -72,7 +72,7 @@ struct icside_state { void __iomem *ioc_base; unsigned int sel; unsigned int type; - ide_hwif_t *hwif[2]; + struct ide_host *host; }; #define ICS_TYPE_A3IN 0 @@ -442,10 +442,9 @@ static void icside_setup_ports(hw_regs_t *hw, void __iomem *base, static int __init icside_register_v5(struct icside_state *state, struct expansion_card *ec) { - ide_hwif_t *hwif; void __iomem *base; + struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); if (!base) @@ -465,17 +464,15 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) icside_setup_ports(&hw, base, &icside_cardinfo_v5, ec); - hwif = ide_find_port(); - if (!hwif) + host = ide_host_alloc(NULL, hws); + if (host == NULL) return -ENODEV; - state->hwif[0] = hwif; + state->host = host; ecard_set_drvdata(ec, state); - idx[0] = hwif->index; - - ide_device_add(idx, NULL, hws); + ide_host_register(host, NULL, hws); return 0; } @@ -492,12 +489,11 @@ static const struct ide_port_info icside_v6_port_info __initdata = { static int __init icside_register_v6(struct icside_state *state, struct expansion_card *ec) { - ide_hwif_t *hwif, *mate; void __iomem *ioc_base, *easi_base; + struct ide_host *host; unsigned int sel = 0; int ret; hw_regs_t hw[2], *hws[] = { &hw[0], NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = icside_v6_port_info; ioc_base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); @@ -537,25 +533,11 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) icside_setup_ports(&hw[0], easi_base, &icside_cardinfo_v6_1, ec); icside_setup_ports(&hw[1], easi_base, &icside_cardinfo_v6_2, ec); - /* - * Find and register the interfaces. - */ - hwif = ide_find_port(); - if (hwif == NULL) + host = ide_host_alloc(&d, hws); + if (host == NULL) return -ENODEV; - hwif->chipset = ide_acorn; - - idx[0] = hwif->index; - - mate = ide_find_port(); - if (mate) { - hws[1] = &hw[1]; - idx[1] = mate->index; - } - - state->hwif[0] = hwif; - state->hwif[1] = mate; + state->host = host; ecard_set_drvdata(ec, state); @@ -565,7 +547,7 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) d.dma_ops = NULL; } - ide_device_add(idx, &d, hws); + ide_host_register(host, &d, hws); return 0; diff --git a/drivers/ide/arm/ide_arm.c b/drivers/ide/arm/ide_arm.c index e9831bbd988a..9efd7a86db45 100644 --- a/drivers/ide/arm/ide_arm.c +++ b/drivers/ide/arm/ide_arm.c @@ -28,10 +28,9 @@ static int __init ide_arm_init(void) { - ide_hwif_t *hwif; + struct ide_host *host; unsigned long base = IDE_ARM_IO, ctl = IDE_ARM_IO + 0x206; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!request_region(base, 8, DRV_NAME)) { printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", @@ -51,12 +50,9 @@ static int __init ide_arm_init(void) hw.irq = IDE_ARM_IRQ; hw.chipset = ide_generic; - hwif = ide_find_port(); - if (hwif) { - idx[0] = hwif->index; - - ide_device_add(idx, NULL, hws); - } + host = ide_host_alloc(NULL, hws); + if (host) + ide_host_register(host, NULL, hws); return 0; } diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 545563bc7e23..24389a571c37 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -347,11 +347,10 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) { struct clk *clk; struct resource *mem, *irq; - ide_hwif_t *hwif; + struct ide_host *host; unsigned long base, rate; int i; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; clk = clk_get(NULL, "IDECLK"); if (IS_ERR(clk)) @@ -393,15 +392,11 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) hw.irq = irq->start; hw.chipset = ide_palm3710; - hwif = ide_find_port(); - if (hwif == NULL) + host = ide_host_alloc(&palm_bk3710_port_info, hws); + if (host == NULL) goto out; - i = hwif->index; - - idx[0] = i; - - ide_device_add(idx, &palm_bk3710_port_info, hws); + ide_host_register(host, &palm_bk3710_port_info, hws); return 0; out: diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index a45c2f694949..11f3307385de 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -32,11 +32,10 @@ static void rapide_setup_ports(hw_regs_t *hw, void __iomem *base, static int __devinit rapide_probe(struct expansion_card *ec, const struct ecard_id *id) { - ide_hwif_t *hwif; void __iomem *base; + struct ide_host *host; int ret; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; ret = ecard_request_resources(ec); if (ret) @@ -53,17 +52,15 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) hw.chipset = ide_generic; hw.dev = &ec->dev; - hwif = ide_find_port(); - if (hwif == NULL) { + host = ide_host_alloc(&rapide_port_info, hws); + if (host == NULL) { ret = -ENOENT; goto release; } - idx[0] = hwif->index; + ide_host_register(host, &rapide_port_info, hws); - ide_device_add(idx, &rapide_port_info, hws); - - ecard_set_drvdata(ec, hwif); + ecard_set_drvdata(ec, host); goto out; release: @@ -74,11 +71,11 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) static void __devexit rapide_remove(struct expansion_card *ec) { - ide_hwif_t *hwif = ecard_get_drvdata(ec); + struct ide_host *host = ecard_get_drvdata(ec); ecard_set_drvdata(ec, NULL); - ide_unregister(hwif); + ide_host_remove(host); ecard_release_resources(ec); } diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 84644e150531..15f76690a48c 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -191,10 +191,8 @@ static const struct ide_port_info h8300_port_info = { static int __init h8300_ide_init(void) { - ide_hwif_t *hwif; - int index; + struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n"); @@ -207,15 +205,11 @@ static int __init h8300_ide_init(void) hw_setup(&hw); - hwif = ide_find_port_slot(&h8300_port_info); - if (hwif == NULL) + host = ide_host_alloc(&h8300_port_info, hws); + if (host == NULL) return -ENOENT; - index = hwif->index; - - idx[0] = index; - - ide_device_add(idx, &h8300_port_info, hws); + ide_host_register(host, &h8300_port_info, hws); return 0; diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index a5c352abff59..e8818362eb46 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -28,27 +28,24 @@ MODULE_PARM_DESC(probe_mask, "probe mask for legacy ISA IDE ports"); static ssize_t store_add(struct class *cls, const char *buf, size_t n) { - ide_hwif_t *hwif; + struct ide_host *host; unsigned int base, ctl; int irq; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[] = { 0xff, 0xff, 0xff, 0xff }; if (sscanf(buf, "%x:%x:%d", &base, &ctl, &irq) != 3) return -EINVAL; - hwif = ide_find_port(); - if (hwif == NULL) - return -ENOENT; - memset(&hw, 0, sizeof(hw)); ide_std_init_ports(&hw, base, ctl); hw.irq = irq; hw.chipset = ide_generic; - idx[0] = hwif->index; + host = ide_host_alloc(NULL, hws); + if (host == NULL) + return -ENOENT; - ide_device_add(idx, NULL, hws); + ide_host_register(host, NULL, hws); return n; }; @@ -89,18 +86,16 @@ static int __init ide_generic_sysfs_init(void) static int __init ide_generic_init(void) { hw_regs_t hw[MAX_HWIFS], *hws[MAX_HWIFS]; - u8 idx[MAX_HWIFS]; + struct ide_host *host; int i; printk(KERN_INFO DRV_NAME ": please use \"probe_mask=0x3f\" module " "parameter for probing all legacy ISA IDE ports\n"); for (i = 0; i < MAX_HWIFS; i++) { - ide_hwif_t *hwif; unsigned long io_addr = ide_default_io_base(i); hws[i] = NULL; - idx[i] = 0xff; if ((probe_mask & (1 << i)) && io_addr) { if (!request_region(io_addr, 8, DRV_NAME)) { @@ -118,23 +113,18 @@ static int __init ide_generic_init(void) continue; } - hwif = ide_find_port(); - if (hwif == NULL) - continue; - - hwif->chipset = ide_generic; - memset(&hw[i], 0, sizeof(hw[i])); ide_std_init_ports(&hw[i], io_addr, io_addr + 0x206); hw[i].irq = ide_default_irq(io_addr); hw[i].chipset = ide_generic; hws[i] = &hw[i]; - idx[i] = i; } } - ide_device_add_all(idx, NULL, hws); + host = ide_host_alloc_all(NULL, hws); + if (host) + ide_host_register(host, NULL, hws); if (ide_generic_sysfs_init()) printk(KERN_ERR DRV_NAME ": failed to create ide_generic " diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index 89cd5cbe8573..4458ca61897a 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -29,7 +29,7 @@ static struct pnp_device_id idepnp_devices[] = { static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { - ide_hwif_t *hwif; + struct ide_host *host; unsigned long base, ctl; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; @@ -59,14 +59,11 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) hw.irq = pnp_irq(dev, 0); hw.chipset = ide_generic; - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - u8 idx[4] = { index, 0xff, 0xff, 0xff }; + host = ide_host_alloc(NULL, hws); + if (host) { + pnp_set_drvdata(dev, host); - pnp_set_drvdata(dev, hwif); - - ide_device_add(idx, NULL, hws); + ide_host_register(host, NULL, hws); return 0; } @@ -79,9 +76,9 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) static void idepnp_remove(struct pnp_dev *dev) { - ide_hwif_t *hwif = pnp_get_drvdata(dev); + struct ide_host *host = pnp_get_drvdata(dev); - ide_unregister(hwif); + ide_host_remove(host); release_region(pnp_port_start(dev, 1), 1); release_region(pnp_port_start(dev, 0), 8); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index c588066295db..84a89561ec0f 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1477,7 +1477,7 @@ static int ide_sysfs_register_port(ide_hwif_t *hwif) * Return the new hwif. If we are out of free slots return NULL. */ -ide_hwif_t *ide_find_port_slot(const struct ide_port_info *d) +static ide_hwif_t *ide_find_port_slot(const struct ide_port_info *d) { ide_hwif_t *hwif; int i; @@ -1523,14 +1523,63 @@ out_found: ide_init_port_data(hwif, i); return hwif; } -EXPORT_SYMBOL_GPL(ide_find_port_slot); -int ide_device_add_all(u8 *idx, const struct ide_port_info *d, hw_regs_t **hws) +struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, + hw_regs_t **hws) +{ + struct ide_host *host; + int i; + + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (host == NULL) + return NULL; + + for (i = 0; i < MAX_HWIFS; i++) { + ide_hwif_t *hwif; + + if (hws[i] == NULL) + continue; + + hwif = ide_find_port_slot(d); + if (hwif) { + hwif->chipset = hws[i]->chipset; + + host->ports[i] = hwif; + host->n_ports++; + } + } + + if (host->n_ports == 0) { + kfree(host); + return NULL; + } + + return host; +} +EXPORT_SYMBOL_GPL(ide_host_alloc_all); + +struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws) +{ + hw_regs_t *hws_all[MAX_HWIFS]; + int i; + + for (i = 0; i < MAX_HWIFS; i++) + hws_all[i] = (i < 4) ? hws[i] : NULL; + + return ide_host_alloc_all(d, hws_all); +} +EXPORT_SYMBOL_GPL(ide_host_alloc); + +int ide_host_register(struct ide_host *host, const struct ide_port_info *d, + hw_regs_t **hws) { ide_hwif_t *hwif, *mate = NULL; + u8 idx[MAX_HWIFS]; int i, rc = 0; for (i = 0; i < MAX_HWIFS; i++) { + idx[i] = host->ports[i] ? host->ports[i]->index : 0xff; + if (idx[i] == 0xff) { mate = NULL; continue; @@ -1626,22 +1675,20 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d, hw_regs_t **hws) return rc; } -EXPORT_SYMBOL_GPL(ide_device_add_all); +EXPORT_SYMBOL_GPL(ide_host_register); -int ide_device_add(u8 *idx, const struct ide_port_info *d, hw_regs_t **hws) +void ide_host_remove(struct ide_host *host) { - hw_regs_t *hws_all[MAX_HWIFS]; - u8 idx_all[MAX_HWIFS]; int i; for (i = 0; i < MAX_HWIFS; i++) { - hws_all[i] = (i < 4) ? hws[i] : NULL; - idx_all[i] = (i < 4) ? idx[i] : 0xff; + if (host->ports[i]) + ide_unregister(host->ports[i]); } - return ide_device_add_all(idx_all, d, hws_all); + kfree(host); } -EXPORT_SYMBOL_GPL(ide_device_add); +EXPORT_SYMBOL_GPL(ide_host_remove); void ide_port_scan(ide_hwif_t *hwif) { @@ -1662,11 +1709,10 @@ void ide_port_scan(ide_hwif_t *hwif) } EXPORT_SYMBOL_GPL(ide_port_scan); -static void ide_legacy_init_one(u8 *idx, hw_regs_t **hws, hw_regs_t *hw, +static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw, u8 port_no, const struct ide_port_info *d, unsigned long config) { - ide_hwif_t *hwif; unsigned long base, ctl; int irq; @@ -1698,31 +1744,29 @@ static void ide_legacy_init_one(u8 *idx, hw_regs_t **hws, hw_regs_t *hw, hw->chipset = d->chipset; hw->config = config; - hwif = ide_find_port_slot(d); - if (hwif) { - hwif->chipset = hw->chipset; - - hws[port_no] = hw; - idx[port_no] = hwif->index; - } + hws[port_no] = hw; } int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config) { - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + struct ide_host *host; hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; memset(&hw, 0, sizeof(hw)); if ((d->host_flags & IDE_HFLAG_QD_2ND_PORT) == 0) - ide_legacy_init_one(idx, hws, &hw[0], 0, d, config); - ide_legacy_init_one(idx, hws, &hw[1], 1, d, config); + ide_legacy_init_one(hws, &hw[0], 0, d, config); + ide_legacy_init_one(hws, &hw[1], 1, d, config); - if (idx[0] == 0xff && idx[1] == 0xff && + if (hws[0] == NULL && hws[1] == NULL && (d->host_flags & IDE_HFLAG_SINGLE)) return -ENOENT; - ide_device_add(idx, d, hws); + host = ide_host_alloc(d, hws); + if (host == NULL) + return -ENOMEM; + + ide_host_register(host, d, hws); return 0; } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 132b504168e9..7e9575d1aee3 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -276,8 +276,6 @@ void ide_unregister(ide_hwif_t *hwif) mutex_unlock(&ide_cfg_mtx); } -EXPORT_SYMBOL(ide_unregister); - void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw) { memcpy(&hwif->io_ports, &hw->io_ports, sizeof(hwif->io_ports)); diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c index c61bc6a1db36..2625667fab4c 100644 --- a/drivers/ide/legacy/buddha.c +++ b/drivers/ide/legacy/buddha.c @@ -150,18 +150,15 @@ static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base, static int __init buddha_init(void) { - ide_hwif_t *hwif; - int i; - struct zorro_dev *z = NULL; + struct ide_host *host; u_long buddha_board = 0; BuddhaType type; - int buddha_num_hwifs; + int buddha_num_hwifs, i; while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { unsigned long board; hw_regs_t hw[MAX_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) { buddha_num_hwifs = BUDDHA_NUM_HWIFS; @@ -226,16 +223,12 @@ fail_base2: buddha_setup_ports(&hw[i], base, ctl, irq_port, ack_intr); - hwif = ide_find_port(); - if (hwif) { - hwif->chipset = ide_generic; - - hws[i] = &hw[i]; - idx[i] = hwif->index; - } + hws[i] = &hw[i]; } - ide_device_add(idx, NULL, hws); + host = ide_host_alloc(NULL, hws); + if (host) + ide_host_register(host, NULL, hws); } return 0; diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 3e2c6125f031..4eb5c3f9fecc 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -112,7 +112,7 @@ static void __init falconide_setup_ports(hw_regs_t *hw) static int __init falconide_init(void) { - ide_hwif_t *hwif; + struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE)) @@ -127,13 +127,10 @@ static int __init falconide_init(void) falconide_setup_ports(&hw); - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - u8 idx[4] = { index, 0xff, 0xff, 0xff }; - + host = ide_host_alloc(&falconide_port_info, hws); + if (host) { ide_get_lock(NULL, NULL); - ide_device_add(idx, &falconide_port_info, hws); + ide_host_register(host, &falconide_port_info, hws); ide_release_lock(); } diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index 7baeefa870fa..13d22bded6b4 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -127,9 +127,9 @@ static int __init gayle_init(void) unsigned long phys_base, res_start, res_n; unsigned long base, ctrlport, irqport; ide_ack_intr_t *ack_intr; + struct ide_host *host; int a4000, i; hw_regs_t hw[GAYLE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!MACH_IS_AMIGA) return -ENODEV; @@ -172,23 +172,17 @@ found: return -EBUSY; for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) { - ide_hwif_t *hwif; - base = (unsigned long)ZTWO_VADDR(phys_base + i * GAYLE_NEXT_PORT); ctrlport = GAYLE_HAS_CONTROL_REG ? (base + GAYLE_CONTROL) : 0; gayle_setup_ports(&hw[i], base, ctrlport, irqport, ack_intr); - hwif = ide_find_port(); - if (hwif) { - hwif->chipset = ide_generic; - - hws[i] = &hw[i]; - idx[i] = hwif->index; - } + hws[i] = &hw[i]; } - ide_device_add(idx, NULL, hws); + host = ide_host_alloc(NULL, hws); + if (host) + ide_host_register(host, NULL, hws); return 0; } diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c index 6310dc50e3c5..5935153ef2ad 100644 --- a/drivers/ide/legacy/ide-4drives.c +++ b/drivers/ide/legacy/ide-4drives.c @@ -28,10 +28,9 @@ static const struct ide_port_info ide_4drives_port_info = { static int __init ide_4drives_init(void) { - ide_hwif_t *hwif, *mate; + struct ide_host *host; unsigned long base = 0x1f0, ctl = 0x3f6; - hw_regs_t hw, *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + hw_regs_t hw, *hws[] = { &hw, &hw, NULL, NULL }; if (probe_4drives == 0) return -ENODEV; @@ -55,21 +54,9 @@ static int __init ide_4drives_init(void) hw.irq = 14; hw.chipset = ide_4drives; - hwif = ide_find_port(); - if (hwif) { - hwif->chipset = ide_4drives; - - hws[0] = &hw; - idx[0] = hwif->index; - } - - mate = ide_find_port(); - if (mate) { - hws[1] = &hw; - idx[1] = mate->index; - } - - ide_device_add(idx, &ide_4drives_port_info, hws); + host = ide_host_alloc(&ide_4drives_port_info, hws); + if (host) + ide_host_register(host, &ide_4drives_port_info, hws); return 0; } diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index f93d5454ebf8..1a4b9e6887fa 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -74,7 +74,7 @@ INT_MODULE_PARM(pc_debug, 0); typedef struct ide_info_t { struct pcmcia_device *p_dev; - ide_hwif_t *hwif; + struct ide_host *host; int ndev; dev_node_t node; } ide_info_t; @@ -132,7 +132,7 @@ static int ide_probe(struct pcmcia_device *link) static void ide_detach(struct pcmcia_device *link) { ide_info_t *info = link->priv; - ide_hwif_t *hwif = info->hwif; + ide_hwif_t *hwif = info->host->ports[0]; unsigned long data_addr, ctl_addr; DEBUG(0, "ide_detach(0x%p)\n", link); @@ -157,13 +157,13 @@ static const struct ide_port_info idecs_port_info = { .host_flags = IDE_HFLAG_NO_DMA, }; -static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl, +static struct ide_host *idecs_register(unsigned long io, unsigned long ctl, unsigned long irq, struct pcmcia_device *handle) { + struct ide_host *host; ide_hwif_t *hwif; int i; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!request_region(io, 8, DRV_NAME)) { printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", @@ -184,26 +184,26 @@ static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl, hw.chipset = ide_pci; hw.dev = &handle->dev; - hwif = ide_find_port(); - if (hwif == NULL) + host = ide_host_alloc(&idecs_port_info, hws); + if (host == NULL) goto out_release; - idx[0] = hwif->index; + ide_host_register(host, &idecs_port_info, hws); - ide_device_add(idx, &idecs_port_info, hws); + hwif = host->ports[0]; if (hwif->present) - return hwif; + return host; /* retry registration in case device is still spinning up */ for (i = 0; i < 10; i++) { msleep(100); ide_port_scan(hwif); if (hwif->present) - return hwif; + return host; } - return hwif; + return host; out_release: release_region(ctl, 1); @@ -235,7 +235,7 @@ static int ide_config(struct pcmcia_device *link) cistpl_cftable_entry_t *cfg; int pass, last_ret = 0, last_fn = 0, is_kme = 0; unsigned long io_base, ctl_base; - ide_hwif_t *hwif; + struct ide_host *host; DEBUG(0, "ide_config(0x%p)\n", link); @@ -330,21 +330,21 @@ static int ide_config(struct pcmcia_device *link) if (is_kme) outb(0x81, ctl_base+1); - hwif = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link); - if (hwif == NULL && link->io.NumPorts1 == 0x20) { + host = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link); + if (host == NULL && link->io.NumPorts1 == 0x20) { outb(0x02, ctl_base + 0x10); - hwif = idecs_register(io_base + 0x10, ctl_base + 0x10, + host = idecs_register(io_base + 0x10, ctl_base + 0x10, link->irq.AssignedIRQ, link); } - if (hwif == NULL) + if (host == NULL) goto failed; info->ndev = 1; - sprintf(info->node.dev_name, "hd%c", 'a' + hwif->index * 2); - info->node.major = hwif->major; + sprintf(info->node.dev_name, "hd%c", 'a' + host->ports[0]->index * 2); + info->node.major = host->ports[0]->major; info->node.minor = 0; - info->hwif = hwif; + info->host = host; link->dev_node = &info->node; printk(KERN_INFO "ide-cs: %s: Vpp = %d.%d\n", info->node.dev_name, link->conf.Vpp / 10, link->conf.Vpp % 10); @@ -375,15 +375,15 @@ failed: static void ide_release(struct pcmcia_device *link) { ide_info_t *info = link->priv; - ide_hwif_t *hwif = info->hwif; + struct ide_host *host = info->host; DEBUG(0, "ide_release(0x%p)\n", link); - if (info->ndev) { + if (info->ndev) /* FIXME: if this fails we need to queue the cleanup somehow -- need to investigate the required PCMCIA magic */ - ide_unregister(hwif); - } + ide_host_remove(host); + info->ndev = 0; pcmcia_disable_device(link); diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index 3d71e336a221..58a942c6a131 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -52,11 +52,10 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) { struct resource *res_base, *res_alt, *res_irq; void __iomem *base, *alt_base; - ide_hwif_t *hwif; struct pata_platform_info *pdata; + struct ide_host *host; int ret = 0, mmio = 0; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = platform_ide_port_info; pdata = pdev->dev.platform_data; @@ -93,12 +92,6 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) res_alt->start, res_alt->end - res_alt->start + 1); } - hwif = ide_find_port(); - if (!hwif) { - ret = -ENODEV; - goto out; - } - memset(&hw, 0, sizeof(hw)); plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start); hw.dev = &pdev->dev; @@ -106,11 +99,15 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) if (mmio) d.host_flags |= IDE_HFLAG_MMIO; - idx[0] = hwif->index; + host = ide_host_alloc(&d, hws); + if (host == NULL) { + ret = -ENODEV; + goto out; + } - ide_device_add(idx, &d, hws); + ide_host_register(host, &d, hws); - platform_set_drvdata(pdev, hwif); + platform_set_drvdata(pdev, host); return 0; @@ -120,9 +117,9 @@ out: static int __devexit plat_ide_remove(struct platform_device *pdev) { - ide_hwif_t *hwif = pdev->dev.driver_data; + struct ide_host *host = pdev->dev.driver_data; - ide_unregister(hwif); + ide_host_remove(host); return 0; } diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c index d839df2239fc..b49cf8c2b91a 100644 --- a/drivers/ide/legacy/macide.c +++ b/drivers/ide/legacy/macide.c @@ -91,8 +91,8 @@ static const char *mac_ide_name[] = static int __init macide_init(void) { - ide_hwif_t *hwif; ide_ack_intr_t *ack_intr; + struct ide_host *host; unsigned long base; int irq; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; @@ -125,13 +125,9 @@ static int __init macide_init(void) macide_setup_ports(&hw, base, irq, ack_intr); - hwif = ide_find_port(); - if (hwif) { - u8 index = hwif->index; - u8 idx[4] = { index, 0xff, 0xff, 0xff }; - - ide_device_add(idx, NULL, hws); - } + host = ide_host_alloc(NULL, hws); + if (host) + ide_host_register(host, NULL, hws); return 0; } diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index 2dc306f852a6..8fb4438a6afd 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -131,10 +131,9 @@ static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={ static int __init q40ide_init(void) { + struct ide_host *host; int i; - ide_hwif_t *hwif; hw_regs_t hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (!MACH_IS_Q40) return -ENODEV; @@ -158,16 +157,12 @@ static int __init q40ide_init(void) q40_ide_setup_ports(&hw[i], pcide_bases[i], NULL, q40ide_default_irq(pcide_bases[i])); - hwif = ide_find_port(); - if (hwif) { - hwif->chipset = ide_generic; - - hws[i] = &hw[i]; - idx[i] = hwif->index; - } + hws[i] = &hw[i]; } - ide_device_add(idx, &q40ide_port_info, hws); + host = ide_host_alloc(&q40ide_port_info, hws); + if (host) + ide_host_register(host, &q40ide_port_info, hws); return 0; } diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index ed1c9a134079..903c628bddd0 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -563,11 +563,10 @@ static int au_ide_probe(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); _auide_hwif *ahwif = &auide_hwif; - ide_hwif_t *hwif; struct resource *res; + struct ide_host *host; int ret = 0; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; #if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA) char *mode = "MWDMA2"; @@ -604,25 +603,23 @@ static int au_ide_probe(struct device *dev) goto out; } - hwif = ide_find_port(); - if (hwif == NULL) { - ret = -ENOENT; - goto out; - } - memset(&hw, 0, sizeof(hw)); auide_setup_ports(&hw, ahwif); hw.irq = ahwif->irq; hw.dev = dev; hw.chipset = ide_au1xxx; - auide_hwif.hwif = hwif; + host = ide_host_alloc(&au1xxx_port_info, hws); + if (host == NULL) { + ret = -ENOENT; + goto out; + } - idx[0] = hwif->index; + ide_host_register(host, &au1xxx_port_info, hws); - ide_device_add(idx, &au1xxx_port_info, hws); + auide_hwif.hwif = host->ports[0]; - dev_set_drvdata(dev, hwif); + dev_set_drvdata(dev, host); printk(KERN_INFO "Au1xxx IDE(builtin) configured for %s\n", mode ); @@ -634,10 +631,10 @@ static int au_ide_remove(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct resource *res; - ide_hwif_t *hwif = dev_get_drvdata(dev); + struct ide_host *host = dev_get_drvdata(dev); _auide_hwif *ahwif = &auide_hwif; - ide_unregister(hwif); + ide_host_remove(host); iounmap((void *)ahwif->regbase); diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index c1ffb83a2de7..b12d9d224831 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -72,12 +72,11 @@ static const struct ide_port_info swarm_port_info = { */ static int __devinit swarm_ide_probe(struct device *dev) { - ide_hwif_t *hwif; u8 __iomem *base; + struct ide_host *host; phys_t offset, size; int i; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[] = { 0xff, 0xff, 0xff, 0xff }; if (!SIBYTE_HAVE_IDE) return -ENODEV; @@ -116,15 +115,13 @@ static int __devinit swarm_ide_probe(struct device *dev) hw.irq = K_INT_GB_IDE; hw.chipset = ide_generic; - hwif = ide_find_port_slot(&swarm_port_info); - if (hwif == NULL) + host = ide_host_alloc(&swarm_port_info, hws); + if (host == NULL) goto err; - idx[0] = hwif->index; + ide_host_register(host, &swarm_port_info, hws); - ide_device_add(idx, &swarm_port_info, hws); - - dev_set_drvdata(dev, hwif); + dev_set_drvdata(dev, host); return 0; err: diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index ccde1e444e13..013697b8cef4 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -180,11 +180,6 @@ static u8 recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */ static DEFINE_SPINLOCK(cmd640_lock); -/* - * These are initialized to point at the devices we control - */ -static ide_hwif_t *cmd_hwif0, *cmd_hwif1; - /* * Interface to access cmd640x registers */ @@ -714,11 +709,11 @@ static int cmd640x_init_one(unsigned long base, unsigned long ctl) */ static int __init cmd640x_init(void) { + struct ide_host *host; int second_port_cmd640 = 0, rc; const char *bus_type, *port2; u8 b, cfr; hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; if (cmd640_vlb && probe_for_cmd640_vlb()) { bus_type = "VLB"; @@ -781,17 +776,10 @@ static int __init cmd640x_init(void) printk(KERN_INFO "cmd640: buggy cmd640%c interface on %s, config=0x%02x" "\n", 'a' + cmd640_chip_version - 1, bus_type, cfr); - cmd_hwif0 = ide_find_port(); - /* * Initialize data for primary port */ - if (cmd_hwif0) { - cmd_hwif0->chipset = ide_cmd640; - - hws[0] = &hw[0]; - idx[0] = cmd_hwif0->index; - } + hws[0] = &hw[0]; /* * Ensure compatibility by always using the slowest timings @@ -831,13 +819,9 @@ static int __init cmd640x_init(void) /* * Initialize data for secondary cmd640 port, if enabled */ - if (second_port_cmd640) { - cmd_hwif1 = ide_find_port(); - if (cmd_hwif1) { - hws[1] = &hw[1]; - idx[1] = cmd_hwif1->index; - } - } + if (second_port_cmd640) + hws[1] = &hw[1]; + printk(KERN_INFO "cmd640: %sserialized, secondary interface %s\n", second_port_cmd640 ? "" : "not ", port2); @@ -845,7 +829,9 @@ static int __init cmd640x_init(void) cmd640_dump_regs(); #endif - ide_device_add(idx, &cmd640_port_info, hws); + host = ide_host_alloc(&cmd640_port_info, hws); + if (host) + ide_host_register(host, &cmd640_port_info, hws); return 1; } diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index e8e7df1915bf..b8ec06d22c61 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -114,9 +114,9 @@ static const struct ide_port_info cyrix_chipsets[] __devinitdata = { static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) { + struct ide_host *host; const struct ide_port_info *d = &cyrix_chipsets[id->driver_data]; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; ide_setup_pci_noise(dev, d); @@ -138,9 +138,11 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic * do all the device setup for us */ - ide_pci_setup_ports(dev, d, 14, &idx[0], &hw[0], &hws[0]); + ide_pci_setup_ports(dev, d, 14, &hw[0], &hws[0]); - ide_device_add(idx, d, hws); + host = ide_host_alloc(d, hws); + if (host) + ide_host_register(host, d, hws); return 0; } diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c index 33fe15db408a..5eb9d9325184 100644 --- a/drivers/ide/pci/delkin_cb.c +++ b/drivers/ide/pci/delkin_cb.c @@ -56,11 +56,10 @@ static const struct ide_port_info delkin_cb_port_info = { static int __devinit delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) { + struct ide_host *host; unsigned long base; - ide_hwif_t *hwif = NULL; int i, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; rc = pci_enable_device(dev); if (rc) { @@ -87,17 +86,13 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) hw.dev = &dev->dev; hw.chipset = ide_pci; /* this enables IRQ sharing */ - hwif = ide_find_port(); - if (hwif == NULL) + host = ide_host_alloc(&delkin_cb_port_info, hws); + if (host == NULL) goto out_disable; - i = hwif->index; + ide_host_register(host, &delkin_cb_port_info, hws); - idx[0] = i; - - ide_device_add(idx, &delkin_cb_port_info, hws); - - pci_set_drvdata(dev, hwif); + pci_set_drvdata(dev, host); return 0; @@ -110,9 +105,9 @@ out_disable: static void delkin_cb_remove (struct pci_dev *dev) { - ide_hwif_t *hwif = pci_get_drvdata(dev); + struct ide_host *host = pci_get_drvdata(dev); - ide_unregister(hwif); + ide_host_remove(host); pci_release_regions(dev); pci_disable_device(dev); diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index 5b1a0e950dfd..d5e2ba6bacd6 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -65,7 +65,7 @@ static struct scc_ports { unsigned long ctl, dma; - ide_hwif_t *hwif; /* for removing port from system */ + struct ide_host *host; /* for removing port from system */ } scc_ports[MAX_HWIFS]; /* PIO transfer mode table */ @@ -586,15 +586,10 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) { struct scc_ports *ports = pci_get_drvdata(dev); - ide_hwif_t *hwif = NULL; + struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; int i; - hwif = ide_find_port_slot(d); - if (hwif == NULL) - return -ENOMEM; - memset(&hw, 0, sizeof(hw)); for (i = 0; i <= 8; i++) hw.io_ports_array[i] = ports->dma + 0x20 + i * 4; @@ -602,9 +597,13 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, hw.dev = &dev->dev; hw.chipset = ide_pci; - idx[0] = hwif->index; + host = ide_host_alloc(d, hws); + if (host == NULL) + return -ENOMEM; - ide_device_add(idx, d, hws); + ide_host_register(host, d, hws); + + ports->host = host; return 0; } @@ -848,8 +847,6 @@ static void __devinit init_hwif_scc(ide_hwif_t *hwif) { struct scc_ports *ports = ide_get_hwifdata(hwif); - ports->hwif = hwif; - /* PTERADD */ out_be32((void __iomem *)(hwif->dma_base + 0x018), hwif->dmatable_dma); @@ -932,7 +929,8 @@ static int __devinit scc_init_one(struct pci_dev *dev, const struct pci_device_i static void __devexit scc_remove(struct pci_dev *dev) { struct scc_ports *ports = pci_get_drvdata(dev); - ide_hwif_t *hwif = ports->hwif; + struct ide_host *host = ports->host; + ide_hwif_t *hwif = host->ports[0]; if (hwif->dmatable_cpu) { pci_free_consistent(dev, PRD_ENTRIES * PRD_BYTES, @@ -940,7 +938,7 @@ static void __devexit scc_remove(struct pci_dev *dev) hwif->dmatable_cpu = NULL; } - ide_unregister(hwif); + ide_host_remove(host); iounmap((void*)ports->dma); iounmap((void*)ports->ctl); diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 5598bd5936d9..440f43a86ad3 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -600,9 +600,8 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) unsigned long cmd_base, irqport; unsigned long bar0, cmd_phys_base, ctl; void __iomem *virt_base; - ide_hwif_t *hwif; + struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = sgiioc4_port_info; /* Get the CmdBlk and CtrlBlk Base Registers */ @@ -635,16 +634,14 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) hw.chipset = ide_pci; hw.dev = &dev->dev; - hwif = ide_find_port_slot(&d); - if (hwif == NULL) - goto err; - /* Initializing chipset IRQ Registers */ writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); - idx[0] = hwif->index; + host = ide_host_alloc(&d, hws); + if (host == NULL) + goto err; - if (ide_device_add(idx, &d, hws)) + if (ide_host_register(host, &d, hws)) return -EIO; return 0; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index ee557d10a764..ecd2f28da1ba 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1039,9 +1039,9 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) { struct device_node *np = pmif->node; const int *bidp; + struct ide_host *host; ide_hwif_t *hwif; hw_regs_t *hws[] = { hw, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; struct ide_port_info d = pmac_port_info; pmif->broken_dma = pmif->broken_dma_warn = 0; @@ -1118,13 +1118,13 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) pmif->mdev ? "macio" : "PCI", pmif->aapl_bus_id, pmif->mediabay ? " (mediabay)" : "", hw->irq); - hwif = ide_find_port_slot(&d); - if (hwif == NULL) + host = ide_host_alloc(&d, hws); + if (host == NULL) return -ENOENT; - idx[0] = hwif->index; + ide_host_register(host, &d, hws); - ide_device_add(idx, &d, hws); + hwif = host->ports[0]; return 0; } diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index 804c3ef245f9..1c0c5570dec8 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -289,7 +289,7 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info * } /** - * ide_hwif_configure - configure an IDE interface + * ide_hw_configure - configure a hw_regs_t instance * @dev: PCI device holding interface * @d: IDE port info * @port: port number @@ -300,23 +300,20 @@ static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info * * is done per interface port rather than per PCI device. There may be * more than one port per device. * - * Returns the new hardware interface structure, or NULL on a failure + * Returns zero on success or an error code. */ -static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, - const struct ide_port_info *d, - unsigned int port, int irq, - hw_regs_t *hw) +static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d, + unsigned int port, int irq, hw_regs_t *hw) { unsigned long ctl = 0, base = 0; - ide_hwif_t *hwif; if ((d->host_flags & IDE_HFLAG_ISA_PORTS) == 0) { if (ide_pci_check_iomem(dev, d, 2 * port) || ide_pci_check_iomem(dev, d, 2 * port + 1)) { printk(KERN_ERR "%s: I/O baseregs (BIOS) are reported " "as MEM for port %d!\n", d->name, port); - return NULL; + return -EINVAL; } ctl = pci_resource_start(dev, 2*port+1); @@ -330,7 +327,7 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, if (!base || !ctl) { printk(KERN_ERR "%s: bad PCI BARs for port %d, skipping\n", d->name, port); - return NULL; + return -EINVAL; } memset(hw, 0, sizeof(*hw)); @@ -339,13 +336,7 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev, hw->chipset = d->chipset ? d->chipset : ide_pci; ide_std_init_ports(hw, base, ctl | 2); - hwif = ide_find_port_slot(d); - if (hwif == NULL) - return NULL; - - hwif->chipset = hw->chipset; - - return hwif; + return 0; } #ifdef CONFIG_BLK_DEV_IDEDMA_PCI @@ -443,7 +434,6 @@ out: * @dev: PCI device * @d: IDE port info * @pciirq: IRQ line - * @idx: ATA index table to update * @hw: hw_regs_t instances corresponding to this PCI IDE device * @hws: hw_regs_t pointers table to update * @@ -457,10 +447,9 @@ out: */ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, - int pciirq, u8 *idx, hw_regs_t *hw, hw_regs_t **hws) + int pciirq, hw_regs_t *hw, hw_regs_t **hws) { int channels = (d->host_flags & IDE_HFLAG_SINGLE) ? 1 : 2, port; - ide_hwif_t *hwif; u8 tmp; /* @@ -476,12 +465,10 @@ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d, continue; /* port not enabled */ } - hwif = ide_hwif_configure(dev, d, port, pciirq, hw + port); - if (hwif == NULL) + if (ide_hw_configure(dev, d, port, pciirq, hw + port)) continue; *(hws + port) = hw + port; - *(idx + port) = hwif->index; } } EXPORT_SYMBOL_GPL(ide_pci_setup_ports); @@ -554,7 +541,7 @@ out: int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) { - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; + struct ide_host *host; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; int ret; @@ -562,9 +549,11 @@ int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) if (ret >= 0) { /* FIXME: silent failure can happen */ - ide_pci_setup_ports(dev, d, ret, &idx[0], &hw[0], &hws[0]); + ide_pci_setup_ports(dev, d, ret, &hw[0], &hws[0]); - ide_device_add(idx, d, hws); + host = ide_host_alloc(d, hws); + if (host) + ide_host_register(host, d, hws); } return ret; @@ -575,9 +564,9 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, const struct ide_port_info *d) { struct pci_dev *pdev[] = { dev1, dev2 }; + struct ide_host *host; int ret, i; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; - u8 idx[4] = { 0xff, 0xff, 0xff, 0xff }; for (i = 0; i < 2; i++) { ret = do_ide_setup_pci_device(pdev[i], d, !i); @@ -590,11 +579,12 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, goto out; /* FIXME: silent failure can happen */ - ide_pci_setup_ports(pdev[i], d, ret, &idx[i*2], &hw[i*2], - &hws[i*2]); + ide_pci_setup_ports(pdev[i], d, ret, &hw[i*2], &hws[i*2]); } - ide_device_add(idx, d, hws); + host = ide_host_alloc(d, hws); + if (host) + ide_host_register(host, d, hws); out: return ret; } diff --git a/include/linux/ide.h b/include/linux/ide.h index 1286a2275efb..a41ae57fafc5 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -558,6 +558,11 @@ typedef struct hwif_s { #endif } ____cacheline_internodealigned_in_smp ide_hwif_t; +struct ide_host { + ide_hwif_t *ports[MAX_HWIFS]; + unsigned int n_ports; +}; + /* * internal ide interrupt handler type */ @@ -813,13 +818,6 @@ int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *, unsig extern int ide_vlb_clk; extern int ide_pci_clk; -ide_hwif_t *ide_find_port_slot(const struct ide_port_info *); - -static inline ide_hwif_t *ide_find_port(void) -{ - return ide_find_port_slot(NULL); -} - extern int ide_end_request (ide_drive_t *drive, int uptodate, int nrsecs); int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq, int uptodate, int nr_sectors); @@ -1024,7 +1022,7 @@ extern int __ide_pci_register_driver(struct pci_driver *driver, struct module *o #endif void ide_pci_setup_ports(struct pci_dev *, const struct ide_port_info *, int, - u8 *, hw_regs_t *, hw_regs_t **); + hw_regs_t *, hw_regs_t **); void ide_setup_pci_noise(struct pci_dev *, const struct ide_port_info *); #ifdef CONFIG_BLK_DEV_IDEDMA_PCI @@ -1236,8 +1234,11 @@ void ide_undecoded_slave(ide_drive_t *); void ide_port_apply_params(ide_hwif_t *); -int ide_device_add_all(u8 *, const struct ide_port_info *, hw_regs_t **); -int ide_device_add(u8 *, const struct ide_port_info *, hw_regs_t **); +struct ide_host *ide_host_alloc_all(const struct ide_port_info *, hw_regs_t **); +struct ide_host *ide_host_alloc(const struct ide_port_info *, hw_regs_t **); +int ide_host_register(struct ide_host *, const struct ide_port_info *, + hw_regs_t **); +void ide_host_remove(struct ide_host *); int ide_legacy_device_add(const struct ide_port_info *, unsigned long); void ide_port_unregister_devices(ide_hwif_t *); void ide_port_scan(ide_hwif_t *); -- cgit From 6f904d015262dfa43eb1cecc00b0998b4c3543f2 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:57 +0200 Subject: ide: add ide_host_add() helper Add ide_host_add() helper which does ide_host_alloc()+ide_host_register(), then convert ide_setup_pci_device[s](), ide_legacy_device_add() and some host drivers to use it. While at it: * Fix ide_setup_pci_device[s](), ide_arm.c, gayle.c, ide-4drives.c, macide.c, q40ide.c, cmd640.c and cs5520.c to return correct error value. * -ENOENT -> -ENOMEM in rapide.c, ide-h8300.c, ide-generic.c, au1xxx-ide.c and pmac.c * -ENODEV -> -ENOMEM in palm_bk3710.c, ide_platform.c and delkin_cb.c * -1 -> -ENOMEM in ide-pnp.c Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/ide_arm.c | 7 +------ drivers/ide/arm/palm_bk3710.c | 10 ++++------ drivers/ide/arm/rapide.c | 8 ++------ drivers/ide/h8300/ide-h8300.c | 9 +-------- drivers/ide/ide-generic.c | 11 ++++------- drivers/ide/ide-pnp.c | 16 ++++++++-------- drivers/ide/ide-probe.c | 27 +++++++++++++++++++-------- drivers/ide/legacy/buddha.c | 5 +---- drivers/ide/legacy/gayle.c | 7 +------ drivers/ide/legacy/ide-4drives.c | 7 +------ drivers/ide/legacy/ide-cs.c | 8 +++----- drivers/ide/legacy/ide_platform.c | 8 ++------ drivers/ide/legacy/macide.c | 7 +------ drivers/ide/legacy/q40ide.c | 7 +------ drivers/ide/mips/au1xxx-ide.c | 8 ++------ drivers/ide/mips/swarm.c | 10 ++++------ drivers/ide/pci/cmd640.c | 7 +------ drivers/ide/pci/cs5520.c | 7 +------ drivers/ide/pci/delkin_cb.c | 8 +++----- drivers/ide/pci/scc_pata.c | 10 ++++------ drivers/ide/ppc/pmac.c | 9 ++++----- drivers/ide/setup-pci.c | 10 ++-------- include/linux/ide.h | 2 ++ 23 files changed, 72 insertions(+), 136 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/ide_arm.c b/drivers/ide/arm/ide_arm.c index 9efd7a86db45..176532ffae0e 100644 --- a/drivers/ide/arm/ide_arm.c +++ b/drivers/ide/arm/ide_arm.c @@ -28,7 +28,6 @@ static int __init ide_arm_init(void) { - struct ide_host *host; unsigned long base = IDE_ARM_IO, ctl = IDE_ARM_IO + 0x206; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; @@ -50,11 +49,7 @@ static int __init ide_arm_init(void) hw.irq = IDE_ARM_IRQ; hw.chipset = ide_generic; - host = ide_host_alloc(NULL, hws); - if (host) - ide_host_register(host, NULL, hws); - - return 0; + return ide_host_add(NULL, hws, NULL); } module_init(ide_arm_init); diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c index 24389a571c37..65bb4b8fd570 100644 --- a/drivers/ide/arm/palm_bk3710.c +++ b/drivers/ide/arm/palm_bk3710.c @@ -349,7 +349,7 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) struct resource *mem, *irq; struct ide_host *host; unsigned long base, rate; - int i; + int i, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; clk = clk_get(NULL, "IDECLK"); @@ -392,16 +392,14 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev) hw.irq = irq->start; hw.chipset = ide_palm3710; - host = ide_host_alloc(&palm_bk3710_port_info, hws); - if (host == NULL) + rc = ide_host_add(&palm_bk3710_port_info, hws, NULL); + if (rc) goto out; - ide_host_register(host, &palm_bk3710_port_info, hws); - return 0; out: printk(KERN_WARNING "Palm Chip BK3710 IDE Register Fail\n"); - return -ENODEV; + return rc; } /* work with hotplug and coldplug */ diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index 11f3307385de..2bdd8b734afb 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -52,13 +52,9 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) hw.chipset = ide_generic; hw.dev = &ec->dev; - host = ide_host_alloc(&rapide_port_info, hws); - if (host == NULL) { - ret = -ENOENT; + ret = ide_host_add(&rapide_port_info, hws, &host); + if (ret) goto release; - } - - ide_host_register(host, &rapide_port_info, hws); ecard_set_drvdata(ec, host); goto out; diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 15f76690a48c..bde7a585f198 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -191,7 +191,6 @@ static const struct ide_port_info h8300_port_info = { static int __init h8300_ide_init(void) { - struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n"); @@ -205,13 +204,7 @@ static int __init h8300_ide_init(void) hw_setup(&hw); - host = ide_host_alloc(&h8300_port_info, hws); - if (host == NULL) - return -ENOENT; - - ide_host_register(host, &h8300_port_info, hws); - - return 0; + return ide_host_add(&h8300_port_info, hws, NULL); out_busy: printk(KERN_ERR "ide-h8300: IDE I/F resource already used.\n"); diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index e8818362eb46..a7082c28d06f 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -28,9 +28,8 @@ MODULE_PARM_DESC(probe_mask, "probe mask for legacy ISA IDE ports"); static ssize_t store_add(struct class *cls, const char *buf, size_t n) { - struct ide_host *host; unsigned int base, ctl; - int irq; + int irq, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (sscanf(buf, "%x:%x:%d", &base, &ctl, &irq) != 3) @@ -41,11 +40,9 @@ static ssize_t store_add(struct class *cls, const char *buf, size_t n) hw.irq = irq; hw.chipset = ide_generic; - host = ide_host_alloc(NULL, hws); - if (host == NULL) - return -ENOENT; - - ide_host_register(host, NULL, hws); + rc = ide_host_add(NULL, hws, NULL); + if (rc) + return rc; return n; }; diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index 4458ca61897a..bac9b392b689 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -31,6 +31,7 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { struct ide_host *host; unsigned long base, ctl; + int rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; printk(KERN_INFO DRV_NAME ": generic PnP IDE interface\n"); @@ -59,19 +60,18 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) hw.irq = pnp_irq(dev, 0); hw.chipset = ide_generic; - host = ide_host_alloc(NULL, hws); - if (host) { - pnp_set_drvdata(dev, host); + rc = ide_host_add(NULL, hws, &host); + if (rc) + goto out; - ide_host_register(host, NULL, hws); - - return 0; - } + pnp_set_drvdata(dev, host); + return 0; +out: release_region(ctl, 1); release_region(base, 8); - return -1; + return rc; } static void idepnp_remove(struct pnp_dev *dev) diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 84a89561ec0f..17a104b95d54 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1677,6 +1677,24 @@ int ide_host_register(struct ide_host *host, const struct ide_port_info *d, } EXPORT_SYMBOL_GPL(ide_host_register); +int ide_host_add(const struct ide_port_info *d, hw_regs_t **hws, + struct ide_host **hostp) +{ + struct ide_host *host; + + host = ide_host_alloc(d, hws); + if (host == NULL) + return -ENOMEM; + + ide_host_register(host, d, hws); + + if (hostp) + *hostp = host; + + return 0; +} +EXPORT_SYMBOL_GPL(ide_host_add); + void ide_host_remove(struct ide_host *host) { int i; @@ -1749,7 +1767,6 @@ static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw, int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config) { - struct ide_host *host; hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL }; memset(&hw, 0, sizeof(hw)); @@ -1762,12 +1779,6 @@ int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config) (d->host_flags & IDE_HFLAG_SINGLE)) return -ENOENT; - host = ide_host_alloc(d, hws); - if (host == NULL) - return -ENOMEM; - - ide_host_register(host, d, hws); - - return 0; + return ide_host_add(d, hws, NULL); } EXPORT_SYMBOL_GPL(ide_legacy_device_add); diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c index 2625667fab4c..7c2afa97f417 100644 --- a/drivers/ide/legacy/buddha.c +++ b/drivers/ide/legacy/buddha.c @@ -151,7 +151,6 @@ static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base, static int __init buddha_init(void) { struct zorro_dev *z = NULL; - struct ide_host *host; u_long buddha_board = 0; BuddhaType type; int buddha_num_hwifs, i; @@ -226,9 +225,7 @@ fail_base2: hws[i] = &hw[i]; } - host = ide_host_alloc(NULL, hws); - if (host) - ide_host_register(host, NULL, hws); + ide_host_add(NULL, hws, NULL); } return 0; diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index 13d22bded6b4..dd5c467d8dd0 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -127,7 +127,6 @@ static int __init gayle_init(void) unsigned long phys_base, res_start, res_n; unsigned long base, ctrlport, irqport; ide_ack_intr_t *ack_intr; - struct ide_host *host; int a4000, i; hw_regs_t hw[GAYLE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; @@ -180,11 +179,7 @@ found: hws[i] = &hw[i]; } - host = ide_host_alloc(NULL, hws); - if (host) - ide_host_register(host, NULL, hws); - - return 0; + return ide_host_add(NULL, hws, NULL); } module_init(gayle_init); diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c index 5935153ef2ad..c76d55de6996 100644 --- a/drivers/ide/legacy/ide-4drives.c +++ b/drivers/ide/legacy/ide-4drives.c @@ -28,7 +28,6 @@ static const struct ide_port_info ide_4drives_port_info = { static int __init ide_4drives_init(void) { - struct ide_host *host; unsigned long base = 0x1f0, ctl = 0x3f6; hw_regs_t hw, *hws[] = { &hw, &hw, NULL, NULL }; @@ -54,11 +53,7 @@ static int __init ide_4drives_init(void) hw.irq = 14; hw.chipset = ide_4drives; - host = ide_host_alloc(&ide_4drives_port_info, hws); - if (host) - ide_host_register(host, &ide_4drives_port_info, hws); - - return 0; + return ide_host_add(&ide_4drives_port_info, hws, NULL); } module_init(ide_4drives_init); diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index 1a4b9e6887fa..21bfac137844 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -162,7 +162,7 @@ static struct ide_host *idecs_register(unsigned long io, unsigned long ctl, { struct ide_host *host; ide_hwif_t *hwif; - int i; + int i, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!request_region(io, 8, DRV_NAME)) { @@ -184,12 +184,10 @@ static struct ide_host *idecs_register(unsigned long io, unsigned long ctl, hw.chipset = ide_pci; hw.dev = &handle->dev; - host = ide_host_alloc(&idecs_port_info, hws); - if (host == NULL) + rc = ide_host_add(&idecs_port_info, hws, &host); + if (rc) goto out_release; - ide_host_register(host, &idecs_port_info, hws); - hwif = host->ports[0]; if (hwif->present) diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c index 58a942c6a131..051b4ab0f359 100644 --- a/drivers/ide/legacy/ide_platform.c +++ b/drivers/ide/legacy/ide_platform.c @@ -99,13 +99,9 @@ static int __devinit plat_ide_probe(struct platform_device *pdev) if (mmio) d.host_flags |= IDE_HFLAG_MMIO; - host = ide_host_alloc(&d, hws); - if (host == NULL) { - ret = -ENODEV; + ret = ide_host_add(&d, hws, &host); + if (ret) goto out; - } - - ide_host_register(host, &d, hws); platform_set_drvdata(pdev, host); diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c index b49cf8c2b91a..a0bb167980e7 100644 --- a/drivers/ide/legacy/macide.c +++ b/drivers/ide/legacy/macide.c @@ -92,7 +92,6 @@ static const char *mac_ide_name[] = static int __init macide_init(void) { ide_ack_intr_t *ack_intr; - struct ide_host *host; unsigned long base; int irq; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; @@ -125,11 +124,7 @@ static int __init macide_init(void) macide_setup_ports(&hw, base, irq, ack_intr); - host = ide_host_alloc(NULL, hws); - if (host) - ide_host_register(host, NULL, hws); - - return 0; + return ide_host_add(NULL, hws, NULL); } module_init(macide_init); diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index 8fb4438a6afd..4abd8fc78197 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -131,7 +131,6 @@ static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={ static int __init q40ide_init(void) { - struct ide_host *host; int i; hw_regs_t hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL }; @@ -160,11 +159,7 @@ static int __init q40ide_init(void) hws[i] = &hw[i]; } - host = ide_host_alloc(&q40ide_port_info, hws); - if (host) - ide_host_register(host, &q40ide_port_info, hws); - - return 0; + return ide_host_add(&q40ide_port_info, hws, NULL); } module_init(q40ide_init); diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index 903c628bddd0..11b7f61aae40 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -609,13 +609,9 @@ static int au_ide_probe(struct device *dev) hw.dev = dev; hw.chipset = ide_au1xxx; - host = ide_host_alloc(&au1xxx_port_info, hws); - if (host == NULL) { - ret = -ENOENT; + ret = ide_host_add(&au1xxx_port_info, hws, &host); + if (ret) goto out; - } - - ide_host_register(host, &au1xxx_port_info, hws); auide_hwif.hwif = host->ports[0]; diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index b12d9d224831..badf79fc9e3a 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -75,7 +75,7 @@ static int __devinit swarm_ide_probe(struct device *dev) u8 __iomem *base; struct ide_host *host; phys_t offset, size; - int i; + int i, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; if (!SIBYTE_HAVE_IDE) @@ -115,19 +115,17 @@ static int __devinit swarm_ide_probe(struct device *dev) hw.irq = K_INT_GB_IDE; hw.chipset = ide_generic; - host = ide_host_alloc(&swarm_port_info, hws); - if (host == NULL) + rc = ide_host_add(&swarm_port_info, hws, &host); + if (rc) goto err; - ide_host_register(host, &swarm_port_info, hws); - dev_set_drvdata(dev, host); return 0; err: release_resource(&swarm_ide_resource); iounmap(base); - return -ENOMEM; + return rc; } static struct device_driver swarm_ide_driver = { diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index 013697b8cef4..e6c62006ca1a 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -709,7 +709,6 @@ static int cmd640x_init_one(unsigned long base, unsigned long ctl) */ static int __init cmd640x_init(void) { - struct ide_host *host; int second_port_cmd640 = 0, rc; const char *bus_type, *port2; u8 b, cfr; @@ -829,11 +828,7 @@ static int __init cmd640x_init(void) cmd640_dump_regs(); #endif - host = ide_host_alloc(&cmd640_port_info, hws); - if (host) - ide_host_register(host, &cmd640_port_info, hws); - - return 1; + return ide_host_add(&cmd640_port_info, hws, NULL); } module_param_named(probe_vlb, cmd640_vlb, bool, 0); diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index b8ec06d22c61..b03d8ae947e6 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -114,7 +114,6 @@ static const struct ide_port_info cyrix_chipsets[] __devinitdata = { static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - struct ide_host *host; const struct ide_port_info *d = &cyrix_chipsets[id->driver_data]; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; @@ -140,11 +139,7 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic ide_pci_setup_ports(dev, d, 14, &hw[0], &hws[0]); - host = ide_host_alloc(d, hws); - if (host) - ide_host_register(host, d, hws); - - return 0; + return ide_host_add(d, hws, NULL); } static const struct pci_device_id cs5520_pci_tbl[] = { diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c index 5eb9d9325184..f84bfb4f600f 100644 --- a/drivers/ide/pci/delkin_cb.c +++ b/drivers/ide/pci/delkin_cb.c @@ -86,12 +86,10 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) hw.dev = &dev->dev; hw.chipset = ide_pci; /* this enables IRQ sharing */ - host = ide_host_alloc(&delkin_cb_port_info, hws); - if (host == NULL) + rc = ide_host_add(&delkin_cb_port_info, hws, &host); + if (rc) goto out_disable; - ide_host_register(host, &delkin_cb_port_info, hws); - pci_set_drvdata(dev, host); return 0; @@ -99,7 +97,7 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) out_disable: pci_release_regions(dev); pci_disable_device(dev); - return -ENODEV; + return rc; } static void diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c index d5e2ba6bacd6..94a7ab864236 100644 --- a/drivers/ide/pci/scc_pata.c +++ b/drivers/ide/pci/scc_pata.c @@ -588,7 +588,7 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, struct scc_ports *ports = pci_get_drvdata(dev); struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; - int i; + int i, rc; memset(&hw, 0, sizeof(hw)); for (i = 0; i <= 8; i++) @@ -597,11 +597,9 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev, hw.dev = &dev->dev; hw.chipset = ide_pci; - host = ide_host_alloc(d, hws); - if (host == NULL) - return -ENOMEM; - - ide_host_register(host, d, hws); + rc = ide_host_add(d, hws, &host); + if (rc) + return rc; ports->host = host; diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index ecd2f28da1ba..c521bf6e1bf2 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -1043,6 +1043,7 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) ide_hwif_t *hwif; hw_regs_t *hws[] = { hw, NULL, NULL, NULL }; struct ide_port_info d = pmac_port_info; + int rc; pmif->broken_dma = pmif->broken_dma_warn = 0; if (of_device_is_compatible(np, "shasta-ata")) { @@ -1118,11 +1119,9 @@ static int __devinit pmac_ide_setup_device(pmac_ide_hwif_t *pmif, hw_regs_t *hw) pmif->mdev ? "macio" : "PCI", pmif->aapl_bus_id, pmif->mediabay ? " (mediabay)" : "", hw->irq); - host = ide_host_alloc(&d, hws); - if (host == NULL) - return -ENOENT; - - ide_host_register(host, &d, hws); + rc = ide_host_add(&d, hws, &host); + if (rc) + return rc; hwif = host->ports[0]; diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index 1c0c5570dec8..b15cad58dc81 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -541,7 +541,6 @@ out: int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) { - struct ide_host *host; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; int ret; @@ -551,9 +550,7 @@ int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) /* FIXME: silent failure can happen */ ide_pci_setup_ports(dev, d, ret, &hw[0], &hws[0]); - host = ide_host_alloc(d, hws); - if (host) - ide_host_register(host, d, hws); + ret = ide_host_add(d, hws, NULL); } return ret; @@ -564,7 +561,6 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, const struct ide_port_info *d) { struct pci_dev *pdev[] = { dev1, dev2 }; - struct ide_host *host; int ret, i; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; @@ -582,9 +578,7 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, ide_pci_setup_ports(pdev[i], d, ret, &hw[i*2], &hws[i*2]); } - host = ide_host_alloc(d, hws); - if (host) - ide_host_register(host, d, hws); + ret = ide_host_add(d, hws, NULL); out: return ret; } diff --git a/include/linux/ide.h b/include/linux/ide.h index a41ae57fafc5..764afd94b917 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1238,6 +1238,8 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *, hw_regs_t **); struct ide_host *ide_host_alloc(const struct ide_port_info *, hw_regs_t **); int ide_host_register(struct ide_host *, const struct ide_port_info *, hw_regs_t **); +int ide_host_add(const struct ide_port_info *, hw_regs_t **, + struct ide_host **); void ide_host_remove(struct ide_host *); int ide_legacy_device_add(const struct ide_port_info *, unsigned long); void ide_port_unregister_devices(ide_hwif_t *); -- cgit From 8a69580e1ea9516caada5eed202afd39546e9809 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 23 Jul 2008 19:55:59 +0200 Subject: ide: add ide_host_free() helper (take 2) * Add ide_host_free() helper and convert ide_host_remove() to use it. * Fix handling of ide_host_register() failure in ide_host_add(), icside.c, ide-generic.c, falconide.c and sgiioc4.c. While at it: * Fix handling of ide_host_alloc_all() failure in ide-generic.c. * Fix handling of ide_host_alloc() failure in falconide.c (also return the correct error value if no device is found). v2: * falconide build fix. (From Stephen Rothwell) Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/arm/icside.c | 21 +++++++++++++++++---- drivers/ide/ide-generic.c | 27 +++++++++++++++++++++++---- drivers/ide/ide-probe.c | 23 ++++++++++++++++++++--- drivers/ide/legacy/falconide.c | 22 +++++++++++++++++----- drivers/ide/pci/sgiioc4.c | 14 ++++++++++---- include/linux/ide.h | 1 + 6 files changed, 88 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 6fa58425466a..f575e8341aec 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -445,6 +445,7 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) void __iomem *base; struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; + int ret; base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); if (!base) @@ -472,9 +473,15 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec) ecard_set_drvdata(ec, state); - ide_host_register(host, NULL, hws); + ret = ide_host_register(host, NULL, hws); + if (ret) + goto err_free; return 0; +err_free: + ide_host_free(host); + ecard_set_drvdata(ec, NULL); + return ret; } static const struct ide_port_info icside_v6_port_info __initdata = { @@ -547,11 +554,17 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec) d.dma_ops = NULL; } - ide_host_register(host, &d, hws); + ret = ide_host_register(host, NULL, hws); + if (ret) + goto err_free; return 0; - - out: +err_free: + ide_host_free(host); + if (d.dma_ops) + free_dma(ec->dma); + ecard_set_drvdata(ec, NULL); +out: return ret; } diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c index a7082c28d06f..31d98fec775f 100644 --- a/drivers/ide/ide-generic.c +++ b/drivers/ide/ide-generic.c @@ -84,13 +84,14 @@ static int __init ide_generic_init(void) { hw_regs_t hw[MAX_HWIFS], *hws[MAX_HWIFS]; struct ide_host *host; - int i; + unsigned long io_addr; + int i, rc; printk(KERN_INFO DRV_NAME ": please use \"probe_mask=0x3f\" module " "parameter for probing all legacy ISA IDE ports\n"); for (i = 0; i < MAX_HWIFS; i++) { - unsigned long io_addr = ide_default_io_base(i); + io_addr = ide_default_io_base(i); hws[i] = NULL; @@ -120,14 +121,32 @@ static int __init ide_generic_init(void) } host = ide_host_alloc_all(NULL, hws); - if (host) - ide_host_register(host, NULL, hws); + if (host == NULL) { + rc = -ENOMEM; + goto err; + } + + rc = ide_host_register(host, NULL, hws); + if (rc) + goto err_free; if (ide_generic_sysfs_init()) printk(KERN_ERR DRV_NAME ": failed to create ide_generic " "class\n"); return 0; +err_free: + ide_host_free(host); +err: + for (i = 0; i < MAX_HWIFS; i++) { + if (hws[i] == NULL) + continue; + + io_addr = hws[i]->io_ports.data_addr; + release_region(io_addr + 0x206, 1); + release_region(io_addr, 8); + } + return rc; } module_init(ide_generic_init); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 6d57b7cd5424..0ead4537fc63 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1729,12 +1729,17 @@ int ide_host_add(const struct ide_port_info *d, hw_regs_t **hws, struct ide_host **hostp) { struct ide_host *host; + int rc; host = ide_host_alloc(d, hws); if (host == NULL) return -ENOMEM; - ide_host_register(host, d, hws); + rc = ide_host_register(host, d, hws); + if (rc) { + ide_host_free(host); + return rc; + } if (hostp) *hostp = host; @@ -1743,7 +1748,7 @@ int ide_host_add(const struct ide_port_info *d, hw_regs_t **hws, } EXPORT_SYMBOL_GPL(ide_host_add); -void ide_host_remove(struct ide_host *host) +void ide_host_free(struct ide_host *host) { ide_hwif_t *hwif; int i; @@ -1754,13 +1759,25 @@ void ide_host_remove(struct ide_host *host) if (hwif == NULL) continue; - ide_unregister(hwif); ide_free_port_slot(hwif->index); kfree(hwif); } kfree(host); } +EXPORT_SYMBOL_GPL(ide_host_free); + +void ide_host_remove(struct ide_host *host) +{ + int i; + + for (i = 0; i < MAX_HWIFS; i++) { + if (host->ports[i]) + ide_unregister(host->ports[i]); + } + + ide_host_free(host); +} EXPORT_SYMBOL_GPL(ide_host_remove); void ide_port_scan(ide_hwif_t *hwif) diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c index 4eb5c3f9fecc..724f95073d80 100644 --- a/drivers/ide/legacy/falconide.c +++ b/drivers/ide/legacy/falconide.c @@ -114,9 +114,10 @@ static int __init falconide_init(void) { struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; + int rc; if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE)) - return 0; + return -ENODEV; printk(KERN_INFO "ide: Falcon IDE controller\n"); @@ -128,13 +129,24 @@ static int __init falconide_init(void) falconide_setup_ports(&hw); host = ide_host_alloc(&falconide_port_info, hws); - if (host) { - ide_get_lock(NULL, NULL); - ide_host_register(host, &falconide_port_info, hws); - ide_release_lock(); + if (host == NULL) { + rc = -ENOMEM; + goto err; } + ide_get_lock(NULL, NULL); + rc = ide_host_register(host, &falconide_port_info, hws); + ide_release_lock(); + + if (rc) + goto err_free; + return 0; +err_free: + ide_host_free(host); +err: + release_mem_region(ATA_HD_BASE, 0x40); + return rc; } module_init(falconide_init); diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index 440f43a86ad3..42eef19a18f1 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -603,6 +603,7 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) struct ide_host *host; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; struct ide_port_info d = sgiioc4_port_info; + int rc; /* Get the CmdBlk and CtrlBlk Base Registers */ bar0 = pci_resource_start(dev, 0); @@ -638,17 +639,22 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev) writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); host = ide_host_alloc(&d, hws); - if (host == NULL) + if (host == NULL) { + rc = -ENOMEM; goto err; + } - if (ide_host_register(host, &d, hws)) - return -EIO; + rc = ide_host_register(host, &d, hws); + if (rc) + goto err_free; return 0; +err_free: + ide_host_free(host); err: release_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE); iounmap(virt_base); - return -ENOMEM; + return rc; } static unsigned int __devinit diff --git a/include/linux/ide.h b/include/linux/ide.h index 764afd94b917..46d5bfe2fefb 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1236,6 +1236,7 @@ void ide_port_apply_params(ide_hwif_t *); struct ide_host *ide_host_alloc_all(const struct ide_port_info *, hw_regs_t **); struct ide_host *ide_host_alloc(const struct ide_port_info *, hw_regs_t **); +void ide_host_free(struct ide_host *); int ide_host_register(struct ide_host *, const struct ide_port_info *, hw_regs_t **); int ide_host_add(const struct ide_port_info *, hw_regs_t **, -- cgit From d7c26ebb5bca284ece9db7311a9e180aabae1922 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 23 Jul 2008 19:55:59 +0200 Subject: ide: push pc callback pointer into the ide_drive_t structure Refrain from carrying the callback ptr with every packet command since the callback function is only one anyways. ide_drive_t is probably not the most suitable place for it right now but is the more sane solution. Besides, these structs are going to be reorganized anyways during the generic ide rewrite. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index 46d5bfe2fefb..a28823297edc 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -308,7 +308,7 @@ struct ide_acpi_drive_link; struct ide_acpi_hwif_link; #endif -typedef struct ide_drive_s { +struct ide_drive_s { char name[4]; /* drive name, such as "hda" */ char driver_req[10]; /* requests specific driver */ @@ -400,7 +400,12 @@ typedef struct ide_drive_s { struct list_head list; struct device gendev; struct completion gendev_rel_comp; /* to deal with device release() */ -} ide_drive_t; + + /* callback for packet commands */ + void (*pc_callback)(struct ide_drive_s *); +}; + +typedef struct ide_drive_s ide_drive_t; #define to_ide_device(dev)container_of(dev, ide_drive_t, gendev) -- cgit From 8bcda3bc492c56abed33a8cbf824c410d7ce69f0 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 23 Jul 2008 19:56:00 +0200 Subject: ide: remove pc->callback member from ide_atapi_pc There should be no functionality change resulting from this patch. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index a28823297edc..d8ec8afb4ef3 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -662,8 +662,6 @@ struct ide_atapi_pc { */ u8 pc_buf[256]; - void (*callback)(ide_drive_t *); - /* idetape only */ struct idetape_bh *bh; char *b_data; -- cgit From 3b8ac5398ca8137b41aa5da4d9fad89153450aaa Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 23 Jul 2008 19:56:01 +0200 Subject: ide: add per-device flags Push device flags up into ide_drive_t. There should be no functionality change resulting from this patch. [bart: IDE_FLAG_* -> IDE_AFLAG_*, dev_flags -> atapi_flags] Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- include/linux/ide.h | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index d8ec8afb4ef3..fcf2d1bede08 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -308,6 +308,64 @@ struct ide_acpi_drive_link; struct ide_acpi_hwif_link; #endif +/* ATAPI device flags */ +enum { + IDE_AFLAG_DRQ_INTERRUPT = (1 << 0), + IDE_AFLAG_MEDIA_CHANGED = (1 << 1), + + /* ide-cd */ + /* Drive cannot lock the door. */ + IDE_AFLAG_NO_DOORLOCK = (1 << 2), + /* Drive cannot eject the disc. */ + IDE_AFLAG_NO_EJECT = (1 << 3), + /* Drive is a pre ATAPI 1.2 drive. */ + IDE_AFLAG_PRE_ATAPI12 = (1 << 4), + /* TOC addresses are in BCD. */ + IDE_AFLAG_TOCADDR_AS_BCD = (1 << 5), + /* TOC track numbers are in BCD. */ + IDE_AFLAG_TOCTRACKS_AS_BCD = (1 << 6), + /* + * Drive does not provide data in multiples of SECTOR_SIZE + * when more than one interrupt is needed. + */ + IDE_AFLAG_LIMIT_NFRAMES = (1 << 7), + /* Seeking in progress. */ + IDE_AFLAG_SEEKING = (1 << 8), + /* Saved TOC information is current. */ + IDE_AFLAG_TOC_VALID = (1 << 9), + /* We think that the drive door is locked. */ + IDE_AFLAG_DOOR_LOCKED = (1 << 10), + /* SET_CD_SPEED command is unsupported. */ + IDE_AFLAG_NO_SPEED_SELECT = (1 << 11), + IDE_AFLAG_VERTOS_300_SSD = (1 << 12), + IDE_AFLAG_VERTOS_600_ESD = (1 << 13), + IDE_AFLAG_SANYO_3CD = (1 << 14), + IDE_AFLAG_FULL_CAPS_PAGE = (1 << 15), + IDE_AFLAG_PLAY_AUDIO_OK = (1 << 16), + IDE_AFLAG_LE_SPEED_FIELDS = (1 << 17), + + /* ide-floppy */ + /* Format in progress */ + IDE_AFLAG_FORMAT_IN_PROGRESS = (1 << 18), + /* Avoid commands not supported in Clik drive */ + IDE_AFLAG_CLIK_DRIVE = (1 << 19), + /* Requires BH algorithm for packets */ + IDE_AFLAG_ZIP_DRIVE = (1 << 20), + + /* ide-tape */ + IDE_AFLAG_IGNORE_DSC = (1 << 21), + /* 0 When the tape position is unknown */ + IDE_AFLAG_ADDRESS_VALID = (1 << 22), + /* Device already opened */ + IDE_AFLAG_BUSY = (1 << 23), + /* Attempt to auto-detect the current user block size */ + IDE_AFLAG_DETECT_BS = (1 << 24), + /* Currently on a filemark */ + IDE_AFLAG_FILEMARK = (1 << 25), + /* 0 = no tape is loaded, so we don't rewind after ejecting */ + IDE_AFLAG_MEDIUM_PRESENT = (1 << 26) +}; + struct ide_drive_s { char name[4]; /* drive name, such as "hda" */ char driver_req[10]; /* requests specific driver */ @@ -403,6 +461,8 @@ struct ide_drive_s { /* callback for packet commands */ void (*pc_callback)(struct ide_drive_s *); + + unsigned long atapi_flags; }; typedef struct ide_drive_s ide_drive_t; -- cgit From ea68d270ff55bcdfa5d07697eb68103b5b02c7bb Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 23 Jul 2008 19:56:01 +0200 Subject: ide-floppy: convert to using the new atapi_flags (take 2) while at it, remove PC_FLAG_ZIP_DRIVE from the packed command flags altogether and query the drive type through drive->atapi_flags. v2: ide-floppy fix. There should be no functionality change resulting from this patch. [bart: IDE_FLAG_* -> IDE_AFLAG_*, dev_flags -> atapi_flags] Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 5 ++-- drivers/ide/ide-floppy.c | 73 ++++++++++++++++++------------------------------ include/linux/ide.h | 3 +- 3 files changed, 31 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index f848010c15a5..711a5f6d35e4 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -257,7 +257,7 @@ ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, } /* Send the actual packet */ - if ((pc->flags & PC_FLAG_ZIP_DRIVE) == 0) + if ((drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) == 0) hwif->tp_ops->output_data(drive, NULL, rq->cmd, 12); return ide_started; @@ -302,7 +302,8 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, bcount, dma); /* Issue the packet command */ - if (pc->flags & PC_FLAG_DRQ_INTERRUPT) { + if ((pc->flags & PC_FLAG_DRQ_INTERRUPT) || + (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT)) { ide_execute_command(drive, WIN_PACKETCMD, handler, timeout, NULL); return ide_started; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index cbf84f2e9cce..3d8e6dd0f41e 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -125,26 +125,10 @@ typedef struct ide_floppy_obj { int wp; /* Supports format progress report */ int srfp; - /* Status/Action flags */ - unsigned long flags; } idefloppy_floppy_t; #define IDEFLOPPY_TICKS_DELAY HZ/20 /* default delay for ZIP 100 (50ms) */ -/* Floppy flag bits values. */ -enum { - /* DRQ interrupt device */ - IDEFLOPPY_FLAG_DRQ_INTERRUPT = (1 << 0), - /* Media may have changed */ - IDEFLOPPY_FLAG_MEDIA_CHANGED = (1 << 1), - /* Format in progress */ - IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS = (1 << 2), - /* Avoid commands not supported in Clik drive */ - IDEFLOPPY_FLAG_CLIK_DRIVE = (1 << 3), - /* Requires BH algorithm for packets */ - IDEFLOPPY_FLAG_ZIP_DRIVE = (1 << 4), -}; - /* Defines for the MODE SENSE command */ #define MODE_SENSE_CURRENT 0x00 #define MODE_SENSE_CHANGEABLE 0x01 @@ -429,7 +413,7 @@ static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive) * 40 and 50msec work well. idefloppy_pc_intr will not be actually * used until after the packet is moved in about 50 msec. */ - if (pc->flags & PC_FLAG_ZIP_DRIVE) { + if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) { timeout = floppy->ticks; expiry = &idefloppy_transfer_pc; } else { @@ -649,12 +633,6 @@ static ide_startstop_t idefloppy_do_request(ide_drive_t *drive, return ide_stopped; } - if (floppy->flags & IDEFLOPPY_FLAG_DRQ_INTERRUPT) - pc->flags |= PC_FLAG_DRQ_INTERRUPT; - - if (floppy->flags & IDEFLOPPY_FLAG_ZIP_DRIVE) - pc->flags |= PC_FLAG_ZIP_DRIVE; - pc->rq = rq; return idefloppy_issue_pc(drive, pc); @@ -798,7 +776,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) switch (pc.buf[desc_start + 4] & 0x03) { /* Clik! drive returns this instead of CAPACITY_CURRENT */ case CAPACITY_UNFORMATTED: - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) /* * If it is not a clik drive, break out * (maintains previous driver behaviour) @@ -844,7 +822,7 @@ static int ide_floppy_get_capacity(ide_drive_t *drive) } /* Clik! disk does not support get_flexible_disk_page */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) (void) ide_floppy_get_flexible_disk_page(drive); set_capacity(floppy->disk, floppy->blocks * floppy->bs_factor); @@ -1046,7 +1024,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) drive->pc_callback = ide_floppy_callback; if (((gcw[0] & 0x60) >> 5) == 1) - floppy->flags |= IDEFLOPPY_FLAG_DRQ_INTERRUPT; + drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT; /* * We used to check revisions here. At this point however I'm giving up. * Just assume they are all broken, its easier. @@ -1057,7 +1035,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) * we'll leave the limitation below for the 2.2.x tree. */ if (!strncmp(drive->id->model, "IOMEGA ZIP 100 ATAPI", 20)) { - floppy->flags |= IDEFLOPPY_FLAG_ZIP_DRIVE; + drive->atapi_flags |= IDE_AFLAG_ZIP_DRIVE; /* This value will be visible in the /proc/ide/hdx/settings */ floppy->ticks = IDEFLOPPY_TICKS_DELAY; blk_queue_max_sectors(drive->queue, 64); @@ -1069,7 +1047,7 @@ static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy) */ if (strncmp(drive->id->model, "IOMEGA Clik!", 11) == 0) { blk_queue_max_sectors(drive->queue, 64); - floppy->flags |= IDEFLOPPY_FLAG_CLIK_DRIVE; + drive->atapi_flags |= IDE_AFLAG_CLIK_DRIVE; } (void) ide_floppy_get_capacity(drive); @@ -1158,7 +1136,7 @@ static int idefloppy_open(struct inode *inode, struct file *filp) floppy->openers++; if (floppy->openers == 1) { - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; /* Just in case */ idefloppy_init_pc(&pc); @@ -1185,14 +1163,14 @@ static int idefloppy_open(struct inode *inode, struct file *filp) ret = -EROFS; goto out_put_floppy; } - floppy->flags |= IDEFLOPPY_FLAG_MEDIA_CHANGED; + drive->atapi_flags |= IDE_AFLAG_MEDIA_CHANGED; /* IOMEGA Clik! drives do not support lock/unlock commands */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) { + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) { idefloppy_create_prevent_cmd(&pc, 1); (void) idefloppy_queue_pc_tail(drive, &pc); } check_disk_change(inode->i_bdev); - } else if (floppy->flags & IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS) { + } else if (drive->atapi_flags & IDE_AFLAG_FORMAT_IN_PROGRESS) { ret = -EBUSY; goto out_put_floppy; } @@ -1215,12 +1193,12 @@ static int idefloppy_release(struct inode *inode, struct file *filp) if (floppy->openers == 1) { /* IOMEGA Clik! drives do not support lock/unlock commands */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) { + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) { idefloppy_create_prevent_cmd(&pc, 0); (void) idefloppy_queue_pc_tail(drive, &pc); } - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; } floppy->openers--; @@ -1241,15 +1219,17 @@ static int idefloppy_getgeo(struct block_device *bdev, struct hd_geometry *geo) return 0; } -static int ide_floppy_lockdoor(idefloppy_floppy_t *floppy, - struct ide_atapi_pc *pc, unsigned long arg, unsigned int cmd) +static int ide_floppy_lockdoor(ide_drive_t *drive, struct ide_atapi_pc *pc, + unsigned long arg, unsigned int cmd) { + idefloppy_floppy_t *floppy = drive->driver_data; + if (floppy->openers > 1) return -EBUSY; /* The IOMEGA Clik! Drive doesn't support this command - * no room for an eject mechanism */ - if (!(floppy->flags & IDEFLOPPY_FLAG_CLIK_DRIVE)) { + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) { int prevent = arg ? 1 : 0; if (cmd == CDROMEJECT) @@ -1270,16 +1250,17 @@ static int ide_floppy_lockdoor(idefloppy_floppy_t *floppy, static int ide_floppy_format_unit(idefloppy_floppy_t *floppy, int __user *arg) { - int blocks, length, flags, err = 0; struct ide_atapi_pc pc; + ide_drive_t *drive = floppy->drive; + int blocks, length, flags, err = 0; if (floppy->openers > 1) { /* Don't format if someone is using the disk */ - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; return -EBUSY; } - floppy->flags |= IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags |= IDE_AFLAG_FORMAT_IN_PROGRESS; /* * Send ATAPI_FORMAT_UNIT to the drive. @@ -1303,15 +1284,15 @@ static int ide_floppy_format_unit(idefloppy_floppy_t *floppy, goto out; } - (void) idefloppy_get_sfrp_bit(floppy->drive); + (void) idefloppy_get_sfrp_bit(drive); idefloppy_create_format_unit_cmd(&pc, blocks, length, flags); - if (idefloppy_queue_pc_tail(floppy->drive, &pc)) + if (idefloppy_queue_pc_tail(drive, &pc)) err = -EIO; out: if (err) - floppy->flags &= ~IDEFLOPPY_FLAG_FORMAT_IN_PROGRESS; + drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS; return err; } @@ -1330,7 +1311,7 @@ static int idefloppy_ioctl(struct inode *inode, struct file *file, case CDROMEJECT: /* fall through */ case CDROM_LOCKDOOR: - return ide_floppy_lockdoor(floppy, &pc, arg, cmd); + return ide_floppy_lockdoor(drive, &pc, arg, cmd); case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED: return 0; case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY: @@ -1371,8 +1352,8 @@ static int idefloppy_media_changed(struct gendisk *disk) drive->attach = 0; return 0; } - ret = !!(floppy->flags & IDEFLOPPY_FLAG_MEDIA_CHANGED); - floppy->flags &= ~IDEFLOPPY_FLAG_MEDIA_CHANGED; + ret = !!(drive->atapi_flags & IDE_AFLAG_MEDIA_CHANGED); + drive->atapi_flags &= ~IDE_AFLAG_MEDIA_CHANGED; return ret; } diff --git a/include/linux/ide.h b/include/linux/ide.h index fcf2d1bede08..147bfee1fe70 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -687,8 +687,7 @@ enum { PC_FLAG_WRITING = (1 << 6), /* command timed out */ PC_FLAG_TIMEDOUT = (1 << 7), - PC_FLAG_ZIP_DRIVE = (1 << 8), - PC_FLAG_DRQ_INTERRUPT = (1 << 9), + PC_FLAG_DRQ_INTERRUPT = (1 << 8), }; struct ide_atapi_pc { -- cgit From ac77ef8b03677c8ae8afe77bccc5f6a969193a79 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 23 Jul 2008 19:56:01 +0200 Subject: ide: remove unused PC_FLAG_DRQ_INTERRUPT There should be no functionality change resulting from this patch. Signed-off-by: Borislav Petkov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-atapi.c | 3 +-- include/linux/ide.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 711a5f6d35e4..adf04f99cdeb 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c @@ -302,8 +302,7 @@ ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc, bcount, dma); /* Issue the packet command */ - if ((pc->flags & PC_FLAG_DRQ_INTERRUPT) || - (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT)) { + if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { ide_execute_command(drive, WIN_PACKETCMD, handler, timeout, NULL); return ide_started; diff --git a/include/linux/ide.h b/include/linux/ide.h index 147bfee1fe70..d67ccca2b964 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -687,7 +687,6 @@ enum { PC_FLAG_WRITING = (1 << 6), /* command timed out */ PC_FLAG_TIMEDOUT = (1 << 7), - PC_FLAG_DRQ_INTERRUPT = (1 << 8), }; struct ide_atapi_pc { -- cgit From a5bf6190417cbbf80443a9f71c65b653e13e9982 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 10 Jul 2008 18:38:33 +0300 Subject: UBI: add ubi_sync() interface To flush MTD device caches. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/kapi.c | 24 ++++++++++++++++++++++++ include/linux/mtd/ubi.h | 1 + 2 files changed, 25 insertions(+) (limited to 'include/linux') diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 51508832566d..e65c8e0bcd5d 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -632,3 +632,27 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) return vol->eba_tbl[lnum] >= 0; } EXPORT_SYMBOL_GPL(ubi_is_mapped); + +/** + * ubi_sync - synchronize UBI device buffers. + * @ubi_num: UBI device to synchronize + * + * The underlying MTD device may cache data in hardware or in software. This + * function ensures the caches are flushed. Returns zero in case of success and + * a negative error code in case of failure. + */ +int ubi_sync(int ubi_num) +{ + struct ubi_device *ubi; + + ubi = ubi_get_device(ubi_num); + if (!ubi) + return -ENODEV; + + if (ubi->mtd->sync) + ubi->mtd->sync(ubi->mtd); + + ubi_put_device(ubi); + return 0; +} +EXPORT_SYMBOL_GPL(ubi_sync); diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index f71201d0f3e7..83302bbbddb4 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -152,6 +152,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum); int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum); int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype); int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum); +int ubi_sync(int ubi_num); /* * This function is the same as the 'ubi_leb_read()' function, but it does not -- cgit From 85c6e6e28259e9b58b8984db536c45bc3161f40c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 16 Jul 2008 10:25:56 +0300 Subject: UBI: amend commentaries Hch asked not to use "unit" for sub-systems, let it be so. Also some other commentaries modifications. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 2 +- drivers/mtd/ubi/debug.h | 6 +-- drivers/mtd/ubi/eba.c | 22 +++++------ drivers/mtd/ubi/io.c | 22 +++++------ drivers/mtd/ubi/scan.c | 28 +++++++------- drivers/mtd/ubi/scan.h | 19 +++++---- drivers/mtd/ubi/ubi-media.h | 23 +++++------ drivers/mtd/ubi/ubi.h | 37 +++++++++--------- drivers/mtd/ubi/wl.c | 94 ++++++++++++++++++++++----------------------- include/linux/mtd/ubi.h | 4 +- 10 files changed, 129 insertions(+), 128 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index a5b19944eca8..27271fe32e02 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -524,7 +524,7 @@ out_si: } /** - * io_init - initialize I/O unit for a given UBI device. + * io_init - initialize I/O sub-system for a given UBI device. * @ubi: UBI device description object * * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 8ea99d8c9e1f..7d8d77c31dfe 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -76,21 +76,21 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); #endif /* CONFIG_MTD_UBI_DEBUG_MSG */ #ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA -/* Messages from the eraseblock association unit */ +/* Messages from the eraseblock association sub-system */ #define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_eba(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL -/* Messages from the wear-leveling unit */ +/* Messages from the wear-leveling sub-system */ #define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_wl(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO -/* Messages from the input/output unit */ +/* Messages from the input/output sub-system */ #define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_io(fmt, ...) ({}) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 8dc488fc0cdf..613cd1e51648 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -19,20 +19,20 @@ */ /* - * The UBI Eraseblock Association (EBA) unit. + * The UBI Eraseblock Association (EBA) sub-system. * - * This unit is responsible for I/O to/from logical eraseblock. + * This sub-system is responsible for I/O to/from logical eraseblock. * * Although in this implementation the EBA table is fully kept and managed in * RAM, which assumes poor scalability, it might be (partially) maintained on * flash in future implementations. * - * The EBA unit implements per-logical eraseblock locking. Before accessing a - * logical eraseblock it is locked for reading or writing. The per-logical - * eraseblock locking is implemented by means of the lock tree. The lock tree - * is an RB-tree which refers all the currently locked logical eraseblocks. The - * lock tree elements are &struct ubi_ltree_entry objects. They are indexed by - * (@vol_id, @lnum) pairs. + * The EBA sub-system implements per-logical eraseblock locking. Before + * accessing a logical eraseblock it is locked for reading or writing. The + * per-logical eraseblock locking is implemented by means of the lock tree. The + * lock tree is an RB-tree which refers all the currently locked logical + * eraseblocks. The lock tree elements are &struct ubi_ltree_entry objects. + * They are indexed by (@vol_id, @lnum) pairs. * * EBA also maintains the global sequence counter which is incremented each * time a logical eraseblock is mapped to a physical eraseblock and it is @@ -1128,7 +1128,7 @@ out_unlock_leb: } /** - * ubi_eba_init_scan - initialize the EBA unit using scanning information. + * ubi_eba_init_scan - initialize the EBA sub-system using scanning information. * @ubi: UBI device description object * @si: scanning information * @@ -1143,7 +1143,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) struct ubi_scan_leb *seb; struct rb_node *rb; - dbg_eba("initialize EBA unit"); + dbg_eba("initialize EBA sub-system"); spin_lock_init(&ubi->ltree_lock); mutex_init(&ubi->alc_mutex); @@ -1209,7 +1209,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) ubi->rsvd_pebs += ubi->beb_rsvd_pebs; } - dbg_eba("EBA unit is initialized"); + dbg_eba("EBA sub-system is initialized"); return 0; out_free: diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 4ac11df7b048..561e7b2f96cb 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -20,15 +20,15 @@ */ /* - * UBI input/output unit. + * UBI input/output sub-system. * - * This unit provides a uniform way to work with all kinds of the underlying - * MTD devices. It also implements handy functions for reading and writing UBI - * headers. + * This sub-system provides a uniform way to work with all kinds of the + * underlying MTD devices. It also implements handy functions for reading and + * writing UBI headers. * * We are trying to have a paranoid mindset and not to trust to what we read - * from the flash media in order to be more secure and robust. So this unit - * validates every single header it reads from the flash media. + * from the flash media in order to be more secure and robust. So this + * sub-system validates every single header it reads from the flash media. * * Some words about how the eraseblock headers are stored. * @@ -79,11 +79,11 @@ * 512-byte chunks, we have to allocate one more buffer and copy our VID header * to offset 448 of this buffer. * - * The I/O unit does the following trick in order to avoid this extra copy. - * It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID header - * and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. When the - * VID header is being written out, it shifts the VID header pointer back and - * writes the whole sub-page. + * The I/O sub-system does the following trick in order to avoid this extra + * copy. It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID + * header and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. + * When the VID header is being written out, it shifts the VID header pointer + * back and writes the whole sub-page. */ #include diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 96d410e106ab..892c2ba49777 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -19,9 +19,9 @@ */ /* - * UBI scanning unit. + * UBI scanning sub-system. * - * This unit is responsible for scanning the flash media, checking UBI + * This sub-system is responsible for scanning the flash media, checking UBI * headers and providing complete information about the UBI flash image. * * The scanning information is represented by a &struct ubi_scan_info' object. @@ -103,7 +103,7 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, * non-zero if an inconsistency was found and zero if not. * * Note, UBI does sanity check of everything it reads from the flash media. - * Most of the checks are done in the I/O unit. Here we check that the + * Most of the checks are done in the I/O sub-system. Here we check that the * information in the VID header is consistent to the information in other VID * headers of the same volume. */ @@ -256,8 +256,8 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, * that versions that are close to %0xFFFFFFFF are less then * versions that are close to %0. * - * The UBI WL unit guarantees that the number of pending tasks - * is not greater then %0x7FFFFFFF. So, if the difference + * The UBI WL sub-system guarantees that the number of pending + * tasks is not greater then %0x7FFFFFFF. So, if the difference * between any two versions is greater or equivalent to * %0x7FFFFFFF, there was an overflow and the logical * eraseblock with lower version is actually newer then the one @@ -645,9 +645,9 @@ void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv) * * This function erases physical eraseblock 'pnum', and writes the erase * counter header to it. This function should only be used on UBI device - * initialization stages, when the EBA unit had not been yet initialized. This - * function returns zero in case of success and a negative error code in case - * of failure. + * initialization stages, when the EBA sub-system had not been yet initialized. + * This function returns zero in case of success and a negative error code in + * case of failure. */ int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si, int pnum, int ec) @@ -687,9 +687,10 @@ out_free: * @si: scanning information * * This function returns a free physical eraseblock. It is supposed to be - * called on the UBI initialization stages when the wear-leveling unit is not - * initialized yet. This function picks a physical eraseblocks from one of the - * lists, writes the EC header if it is needed, and removes it from the list. + * called on the UBI initialization stages when the wear-leveling sub-system is + * not initialized yet. This function picks a physical eraseblocks from one of + * the lists, writes the EC header if it is needed, and removes it from the + * list. * * This function returns scanning physical eraseblock information in case of * success and an error code in case of failure. @@ -764,8 +765,9 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum return err; else if (err) { /* - * FIXME: this is actually duty of the I/O unit to initialize - * this, but MTD does not provide enough information. + * FIXME: this is actually duty of the I/O sub-system to + * initialize this, but MTD does not provide enough + * information. */ si->bad_peb_count += 1; return 0; diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h index 966b9b682a42..4e2e3cc0becd 100644 --- a/drivers/mtd/ubi/scan.h +++ b/drivers/mtd/ubi/scan.h @@ -59,16 +59,16 @@ struct ubi_scan_leb { * @leb_count: number of logical eraseblocks in this volume * @vol_type: volume type * @used_ebs: number of used logical eraseblocks in this volume (only for - * static volumes) + * static volumes) * @last_data_size: amount of data in the last logical eraseblock of this - * volume (always equivalent to the usable logical eraseblock size in case of - * dynamic volumes) + * volume (always equivalent to the usable logical eraseblock + * size in case of dynamic volumes) * @data_pad: how many bytes at the end of logical eraseblocks of this volume - * are not used (due to volume alignment) + * are not used (due to volume alignment) * @compat: compatibility flags of this volume * @rb: link in the volume RB-tree * @root: root of the RB-tree containing all the eraseblock belonging to this - * volume (&struct ubi_scan_leb objects) + * volume (&struct ubi_scan_leb objects) * * One object of this type is allocated for each volume during scanning. */ @@ -92,8 +92,8 @@ struct ubi_scan_volume { * @free: list of free physical eraseblocks * @erase: list of physical eraseblocks which have to be erased * @alien: list of physical eraseblocks which should not be used by UBI (e.g., + * those belonging to "preserve"-compatible internal volumes) * @bad_peb_count: count of bad physical eraseblocks - * those belonging to "preserve"-compatible internal volumes) * @vols_found: number of volumes found during scanning * @highest_vol_id: highest volume ID * @alien_peb_count: count of physical eraseblocks in the @alien list @@ -106,8 +106,8 @@ struct ubi_scan_volume { * @ec_count: a temporary variable used when calculating @mean_ec * * This data structure contains the result of scanning and may be used by other - * UBI units to build final UBI data structures, further error-recovery and so - * on. + * UBI sub-systems to build final UBI data structures, further error-recovery + * and so on. */ struct ubi_scan_info { struct rb_root volumes; @@ -132,8 +132,7 @@ struct ubi_device; struct ubi_vid_hdr; /* - * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a - * list. + * ubi_scan_move_to_list - move a PEB from the volume tree to a list. * * @sv: volume scanning information * @seb: scanning eraseblock infprmation diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h index c3185d9fd048..26bb7af9787a 100644 --- a/drivers/mtd/ubi/ubi-media.h +++ b/drivers/mtd/ubi/ubi-media.h @@ -98,10 +98,11 @@ enum { * Compatibility constants used by internal volumes. * * @UBI_COMPAT_DELETE: delete this internal volume before anything is written - * to the flash + * to the flash * @UBI_COMPAT_RO: attach this device in read-only mode * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its - * physical eraseblocks, don't allow the wear-leveling unit to move them + * physical eraseblocks, don't allow the wear-leveling + * sub-system to move them * @UBI_COMPAT_REJECT: reject this UBI image */ enum { @@ -123,7 +124,7 @@ enum { * struct ubi_ec_hdr - UBI erase counter header. * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) * @version: version of UBI implementation which is supposed to accept this - * UBI image + * UBI image * @padding1: reserved for future, zeroes * @ec: the erase counter * @vid_hdr_offset: where the VID header starts @@ -159,20 +160,20 @@ struct ubi_ec_hdr { * struct ubi_vid_hdr - on-flash UBI volume identifier header. * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) * @version: UBI implementation version which is supposed to accept this UBI - * image (%UBI_VERSION) + * image (%UBI_VERSION) * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) * @copy_flag: if this logical eraseblock was copied from another physical - * eraseblock (for wear-leveling reasons) + * eraseblock (for wear-leveling reasons) * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, - * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) * @vol_id: ID of this volume * @lnum: logical eraseblock number * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be - * removed, kept only for not breaking older UBI users) + * removed, kept only for not breaking older UBI users) * @data_size: how many bytes of data this logical eraseblock contains * @used_ebs: total number of used logical eraseblocks in this volume * @data_pad: how many bytes at the end of this physical eraseblock are not - * used + * used * @data_crc: CRC checksum of the data stored in this logical eraseblock * @padding1: reserved for future, zeroes * @sqnum: sequence number @@ -248,9 +249,9 @@ struct ubi_ec_hdr { * The @data_crc field contains the CRC checksum of the contents of the logical * eraseblock if this is a static volume. In case of dynamic volumes, it does * not contain the CRC checksum as a rule. The only exception is when the - * data of the physical eraseblock was moved by the wear-leveling unit, then - * the wear-leveling unit calculates the data CRC and stores it in the - * @data_crc field. And of course, the @copy_flag is %in this case. + * data of the physical eraseblock was moved by the wear-leveling sub-system, + * then the wear-leveling sub-system calculates the data CRC and stores it in + * the @data_crc field. And of course, the @copy_flag is %in this case. * * The @data_size field is used only for static volumes because UBI has to know * how many bytes of data are stored in this eraseblock. For dynamic volumes, diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 940f6b7deec3..1fc32c863b78 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -74,15 +74,15 @@ #define UBI_IO_RETRIES 3 /* - * Error codes returned by the I/O unit. + * Error codes returned by the I/O sub-system. * * UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only - * 0xFF bytes + * %0xFF bytes * UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a - * valid erase counter header, and the rest are %0xFF bytes + * valid erase counter header, and the rest are %0xFF bytes * UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC) * UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or - * CRC) + * CRC) * UBI_IO_BITFLIPS: bit-flips were detected and corrected */ enum { @@ -99,9 +99,9 @@ enum { * @ec: erase counter * @pnum: physical eraseblock number * - * This data structure is used in the WL unit. Each physical eraseblock has a - * corresponding &struct wl_entry object which may be kept in different - * RB-trees. See WL unit for details. + * This data structure is used in the WL sub-system. Each physical eraseblock + * has a corresponding &struct wl_entry object which may be kept in different + * RB-trees. See WL sub-system for details. */ struct ubi_wl_entry { struct rb_node rb; @@ -118,10 +118,10 @@ struct ubi_wl_entry { * @mutex: read/write mutex to implement read/write access serialization to * the (@vol_id, @lnum) logical eraseblock * - * This data structure is used in the EBA unit to implement per-LEB locking. - * When a logical eraseblock is being locked - corresponding + * This data structure is used in the EBA sub-system to implement per-LEB + * locking. When a logical eraseblock is being locked - corresponding * &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree). - * See EBA unit for details. + * See EBA sub-system for details. */ struct ubi_ltree_entry { struct rb_node rb; @@ -225,7 +225,7 @@ struct ubi_volume { #ifdef CONFIG_MTD_UBI_GLUEBI /* * Gluebi-related stuff may be compiled out. - * TODO: this should not be built into UBI but should be a separate + * Note: this should not be built into UBI but should be a separate * ubimtd driver which works on top of UBI and emulates MTD devices. */ struct ubi_volume_desc *gluebi_desc; @@ -235,8 +235,7 @@ struct ubi_volume { }; /** - * struct ubi_volume_desc - descriptor of the UBI volume returned when it is - * opened. + * struct ubi_volume_desc - UBI volume descriptor returned when it is opened. * @vol: reference to the corresponding volume description object * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE) */ @@ -316,11 +315,11 @@ struct ubi_wl_entry; * @ro_mode: if the UBI device is in read-only mode * @leb_size: logical eraseblock size * @leb_start: starting offset of logical eraseblocks within physical - * eraseblocks + * eraseblocks * @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size * @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size * @vid_hdr_offset: starting offset of the volume identifier header (might be - * unaligned) + * unaligned) * @vid_hdr_aloffset: starting offset of the VID header aligned to * @hdrs_min_io_size * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset @@ -356,16 +355,16 @@ struct ubi_device { struct mutex volumes_mutex; int max_ec; - /* TODO: mean_ec is not updated run-time, fix */ + /* Note, mean_ec is not updated run-time - should be fixed */ int mean_ec; - /* EBA unit's stuff */ + /* EBA sub-system's stuff */ unsigned long long global_sqnum; spinlock_t ltree_lock; struct rb_root ltree; struct mutex alc_mutex; - /* Wear-leveling unit's stuff */ + /* Wear-leveling sub-system's stuff */ struct rb_root used; struct rb_root free; struct rb_root scrub; @@ -388,7 +387,7 @@ struct ubi_device { int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; - /* I/O unit's stuff */ + /* I/O sub-system's stuff */ long long flash_size; int peb_count; int peb_size; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index cc8fe2934d2b..761952ba125b 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -19,22 +19,22 @@ */ /* - * UBI wear-leveling unit. + * UBI wear-leveling sub-system. * - * This unit is responsible for wear-leveling. It works in terms of physical - * eraseblocks and erase counters and knows nothing about logical eraseblocks, - * volumes, etc. From this unit's perspective all physical eraseblocks are of - * two types - used and free. Used physical eraseblocks are those that were - * "get" by the 'ubi_wl_get_peb()' function, and free physical eraseblocks are - * those that were put by the 'ubi_wl_put_peb()' function. + * This sub-system is responsible for wear-leveling. It works in terms of + * physical* eraseblocks and erase counters and knows nothing about logical + * eraseblocks, volumes, etc. From this sub-system's perspective all physical + * eraseblocks are of two types - used and free. Used physical eraseblocks are + * those that were "get" by the 'ubi_wl_get_peb()' function, and free physical + * eraseblocks are those that were put by the 'ubi_wl_put_peb()' function. * * Physical eraseblocks returned by 'ubi_wl_get_peb()' have only erase counter - * header. The rest of the physical eraseblock contains only 0xFF bytes. + * header. The rest of the physical eraseblock contains only %0xFF bytes. * - * When physical eraseblocks are returned to the WL unit by means of the + * When physical eraseblocks are returned to the WL sub-system by means of the * 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is * done asynchronously in context of the per-UBI device background thread, - * which is also managed by the WL unit. + * which is also managed by the WL sub-system. * * The wear-leveling is ensured by means of moving the contents of used * physical eraseblocks with low erase counter to free physical eraseblocks @@ -43,34 +43,36 @@ * The 'ubi_wl_get_peb()' function accepts data type hints which help to pick * an "optimal" physical eraseblock. For example, when it is known that the * physical eraseblock will be "put" soon because it contains short-term data, - * the WL unit may pick a free physical eraseblock with low erase counter, and - * so forth. + * the WL sub-system may pick a free physical eraseblock with low erase + * counter, and so forth. * - * If the WL unit fails to erase a physical eraseblock, it marks it as bad. + * If the WL sub-system fails to erase a physical eraseblock, it marks it as + * bad. * - * This unit is also responsible for scrubbing. If a bit-flip is detected in a - * physical eraseblock, it has to be moved. Technically this is the same as - * moving it for wear-leveling reasons. + * This sub-system is also responsible for scrubbing. If a bit-flip is detected + * in a physical eraseblock, it has to be moved. Technically this is the same + * as moving it for wear-leveling reasons. * - * As it was said, for the UBI unit all physical eraseblocks are either "free" - * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used - * eraseblocks are kept in a set of different RB-trees: @wl->used, + * As it was said, for the UBI sub-system all physical eraseblocks are either + * "free" or "used". Free eraseblock are kept in the @wl->free RB-tree, while + * used eraseblocks are kept in a set of different RB-trees: @wl->used, * @wl->prot.pnum, @wl->prot.aec, and @wl->scrub. * * Note, in this implementation, we keep a small in-RAM object for each physical * eraseblock. This is surely not a scalable solution. But it appears to be good * enough for moderately large flashes and it is simple. In future, one may - * re-work this unit and make it more scalable. + * re-work this sub-system and make it more scalable. * - * At the moment this unit does not utilize the sequence number, which was - * introduced relatively recently. But it would be wise to do this because the - * sequence number of a logical eraseblock characterizes how old is it. For + * At the moment this sub-system does not utilize the sequence number, which + * was introduced relatively recently. But it would be wise to do this because + * the sequence number of a logical eraseblock characterizes how old is it. For * example, when we move a PEB with low erase counter, and we need to pick the * target PEB, we pick a PEB with the highest EC if our PEB is "old" and we * pick target PEB with an average EC if our PEB is not very "old". This is a - * room for future re-works of the WL unit. + * room for future re-works of the WL sub-system. * - * FIXME: looks too complex, should be simplified (later). + * Note: the stuff with protection trees looks too complex and is difficult to + * understand. Should be fixed. */ #include @@ -92,20 +94,21 @@ /* * Maximum difference between two erase counters. If this threshold is - * exceeded, the WL unit starts moving data from used physical eraseblocks with - * low erase counter to free physical eraseblocks with high erase counter. + * exceeded, the WL sub-system starts moving data from used physical + * eraseblocks with low erase counter to free physical eraseblocks with high + * erase counter. */ #define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD /* - * When a physical eraseblock is moved, the WL unit has to pick the target + * When a physical eraseblock is moved, the WL sub-system has to pick the target * physical eraseblock to move to. The simplest way would be just to pick the * one with the highest erase counter. But in certain workloads this could lead * to an unlimited wear of one or few physical eraseblock. Indeed, imagine a * situation when the picked physical eraseblock is constantly erased after the * data is written to it. So, we have a constant which limits the highest erase - * counter of the free physical eraseblock to pick. Namely, the WL unit does - * not pick eraseblocks with erase counter greater then the lowest erase + * counter of the free physical eraseblock to pick. Namely, the WL sub-system + * does not pick eraseblocks with erase counter greater then the lowest erase * counter plus %WL_FREE_MAX_DIFF. */ #define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD) @@ -123,11 +126,11 @@ * @abs_ec: the absolute erase counter value when the protection ends * @e: the wear-leveling entry of the physical eraseblock under protection * - * When the WL unit returns a physical eraseblock, the physical eraseblock is - * protected from being moved for some "time". For this reason, the physical - * eraseblock is not directly moved from the @wl->free tree to the @wl->used - * tree. There is one more tree in between where this physical eraseblock is - * temporarily stored (@wl->prot). + * When the WL sub-system returns a physical eraseblock, the physical + * eraseblock is protected from being moved for some "time". For this reason, + * the physical eraseblock is not directly moved from the @wl->free tree to the + * @wl->used tree. There is one more tree in between where this physical + * eraseblock is temporarily stored (@wl->prot). * * All this protection stuff is needed because: * o we don't want to move physical eraseblocks just after we have given them @@ -175,7 +178,6 @@ struct ubi_wl_prot_entry { * @list: a link in the list of pending works * @func: worker function * @priv: private data of the worker function - * * @e: physical eraseblock to erase * @torture: if the physical eraseblock has to be tortured * @@ -1136,7 +1138,7 @@ out_ro: } /** - * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit. + * ubi_wl_put_peb - return a PEB to the wear-leveling sub-system. * @ubi: UBI device description object * @pnum: physical eraseblock to return * @torture: if this physical eraseblock has to be tortured @@ -1175,11 +1177,11 @@ retry: /* * User is putting the physical eraseblock which was selected * as the target the data is moved to. It may happen if the EBA - * unit already re-mapped the LEB in 'ubi_eba_copy_leb()' but - * the WL unit has not put the PEB to the "used" tree yet, but - * it is about to do this. So we just set a flag which will - * tell the WL worker that the PEB is not needed anymore and - * should be scheduled for erasure. + * sub-system already re-mapped the LEB in 'ubi_eba_copy_leb()' + * but the WL sub-system has not put the PEB to the "used" tree + * yet, but it is about to do this. So we just set a flag which + * will tell the WL worker that the PEB is not needed anymore + * and should be scheduled for erasure. */ dbg_wl("PEB %d is the target of data moving", pnum); ubi_assert(!ubi->move_to_put); @@ -1425,8 +1427,7 @@ static void cancel_pending(struct ubi_device *ubi) } /** - * ubi_wl_init_scan - initialize the wear-leveling unit using scanning - * information. + * ubi_wl_init_scan - initialize the WL sub-system using scanning information. * @ubi: UBI device description object * @si: scanning information * @@ -1583,13 +1584,12 @@ static void protection_trees_destroy(struct ubi_device *ubi) } /** - * ubi_wl_close - close the wear-leveling unit. + * ubi_wl_close - close the wear-leveling sub-system. * @ubi: UBI device description object */ void ubi_wl_close(struct ubi_device *ubi) { - dbg_wl("close the UBI wear-leveling unit"); - + dbg_wl("close the WL sub-system"); cancel_pending(ubi); protection_trees_destroy(ubi); tree_destroy(&ubi->used); diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index 83302bbbddb4..6316fafe5c2a 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -45,13 +45,13 @@ enum { * @size: how many physical eraseblocks are reserved for this volume * @used_bytes: how many bytes of data this volume contains * @used_ebs: how many physical eraseblocks of this volume actually contain any - * data + * data * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * @corrupted: non-zero if the volume is corrupted (static volumes only) * @upd_marker: non-zero if the volume has update marker set * @alignment: volume alignment * @usable_leb_size: how many bytes are available in logical eraseblocks of - * this volume + * this volume * @name_len: volume name length * @name: volume name * @cdev: UBI volume character device major and minor numbers -- cgit From b552068999b0b05087c454e525b30b785c79dc9b Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 23 Apr 2008 10:07:27 -0400 Subject: Remove __DECLARE_SEMAPHORE_GENERIC There are no users of __DECLARE_SEMAPHORE_GENERIC in the kernel Signed-off-by: Matthew Wilcox --- include/linux/semaphore.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/semaphore.h b/include/linux/semaphore.h index 9cae64b00d6b..7415839ac890 100644 --- a/include/linux/semaphore.h +++ b/include/linux/semaphore.h @@ -26,10 +26,8 @@ struct semaphore { .wait_list = LIST_HEAD_INIT((name).wait_list), \ } -#define __DECLARE_SEMAPHORE_GENERIC(name, count) \ - struct semaphore name = __SEMAPHORE_INITIALIZER(name, count) - -#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name, 1) +#define DECLARE_MUTEX(name) \ + struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1) static inline void sema_init(struct semaphore *sem, int val) { -- cgit From e108526e77aa41c89b3be96f75d97615db2b751c Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 23 Jul 2008 21:26:44 -0700 Subject: move memory_read_from_buffer() from fs.h to string.h James Bottomley warns that inclusion of linux/fs.h in a low level driver was always a danger signal. This patch moves memory_read_from_buffer() from fs.h to string.h and fixes includes in existing memory_read_from_buffer() users. Signed-off-by: Akinobu Mita Cc: James Bottomley Cc: Geert Uytterhoeven Cc: Zhang Rui Cc: Bob Moore Cc: Thomas Renninger Cc: Len Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/acpi/system.c | 1 + drivers/zorro/zorro-sysfs.c | 1 - include/linux/fs.h | 2 -- include/linux/string.h | 3 +++ 4 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index d8e3f153b295..91dec448b3ed 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c index 3da712cc7708..5290552d2ef7 100644 --- a/drivers/zorro/zorro-sysfs.c +++ b/drivers/zorro/zorro-sysfs.c @@ -15,7 +15,6 @@ #include #include #include -#include #include "zorro.h" diff --git a/include/linux/fs.h b/include/linux/fs.h index 9c2ac5c0ef5c..ff54ae4933f3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2006,8 +2006,6 @@ extern void simple_release_fs(struct vfsmount **mount, int *count); extern ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos, const void *from, size_t available); -extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos, - const void *from, size_t available); #ifdef CONFIG_MIGRATION extern int buffer_migrate_page(struct address_space *, diff --git a/include/linux/string.h b/include/linux/string.h index efdc44593b52..810d80df0a1d 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -111,5 +111,8 @@ extern void argv_free(char **argv); extern bool sysfs_streq(const char *s1, const char *s2); +extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos, + const void *from, size_t available); + #endif #endif /* _LINUX_STRING_H_ */ -- cgit From 8b05c7e6e159d2f33c9275281b8b909a89eb7c5d Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Wed, 23 Jul 2008 21:26:53 -0700 Subject: add a helper function to test if an object is on the stack lib/debugobjects.c has a function to test if an object is on the stack. The block layer and ide needs it (they need to avoid DMA from/to stack buffers). This patch moves the function to include/linux/sched.h so that everyone can use it. lib/debugobjects.c uses current->stack but this patch uses a task_stack_page() accessor, which is a preferable way to access the stack. Signed-off-by: FUJITA Tomonori Cc: Christoph Lameter Cc: Andy Whitcroft Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 7 +++++++ lib/debugobjects.c | 4 +--- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index dc7e592c473a..6aca4a16e377 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1983,6 +1983,13 @@ static inline unsigned long *end_of_stack(struct task_struct *p) #endif +static inline int object_is_on_stack(void *obj) +{ + void *stack = task_stack_page(current); + + return (obj >= stack) && (obj < (stack + THREAD_SIZE)); +} + extern void thread_info_cache_init(void); /* set thread flags in other task's structures diff --git a/lib/debugobjects.c b/lib/debugobjects.c index 85b18d79be89..f86196390cfd 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -226,15 +226,13 @@ debug_object_fixup(int (*fixup)(void *addr, enum debug_obj_state state), static void debug_object_is_on_stack(void *addr, int onstack) { - void *stack = current->stack; int is_on_stack; static int limit; if (limit > 4) return; - is_on_stack = (addr >= stack && addr < (stack + THREAD_SIZE)); - + is_on_stack = object_is_on_stack(addr); if (is_on_stack == onstack) return; -- cgit From b61bfa3c462671c48a51fb5c31af337c5a996a04 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:26:55 -0700 Subject: mm: move bootmem descriptors definition to a single place There are a lot of places that define either a single bootmem descriptor or an array of them. Use only one central array with MAX_NUMNODES items instead. Signed-off-by: Johannes Weiner Acked-by: Ralf Baechle Cc: Ingo Molnar Cc: Richard Henderson Cc: Russell King Cc: Tony Luck Cc: Hirokazu Takata Cc: Geert Uytterhoeven Cc: Kyle McMartin Cc: Paul Mackerras Cc: Paul Mundt Cc: David S. Miller Cc: Yinghai Lu Cc: Christoph Lameter Cc: Mel Gorman Cc: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/mm/numa.c | 8 ++++---- arch/arm/mm/discontig.c | 34 ++++++++++++++++------------------ arch/ia64/mm/discontig.c | 11 +++++------ arch/m32r/mm/discontig.c | 4 +--- arch/m68k/mm/init.c | 4 +--- arch/mips/sgi-ip27/ip27-memory.c | 4 +--- arch/parisc/mm/init.c | 3 +-- arch/powerpc/mm/numa.c | 3 +-- arch/sh/mm/numa.c | 5 ++--- arch/sparc64/mm/init.c | 3 +-- arch/x86/mm/discontig_32.c | 3 +-- arch/x86/mm/numa_64.c | 4 +--- include/linux/bootmem.h | 2 ++ mm/bootmem.c | 2 ++ mm/page_alloc.c | 4 +--- 15 files changed, 40 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index 10ab7833e83c..a53fda0481ca 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -19,7 +19,6 @@ #include pg_data_t node_data[MAX_NUMNODES]; -bootmem_data_t node_bdata[MAX_NUMNODES]; EXPORT_SYMBOL(node_data); #undef DEBUG_DISCONTIG @@ -141,7 +140,7 @@ setup_memory_node(int nid, void *kernel_end) printk(" not enough mem to reserve NODE_DATA"); return; } - NODE_DATA(nid)->bdata = &node_bdata[nid]; + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; printk(" Detected node memory: start %8lu, end %8lu\n", node_min_pfn, node_max_pfn); @@ -304,8 +303,9 @@ void __init paging_init(void) dma_local_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; for_each_online_node(nid) { - unsigned long start_pfn = node_bdata[nid].node_boot_start >> PAGE_SHIFT; - unsigned long end_pfn = node_bdata[nid].node_low_pfn; + bootmem_data_t *bdata = &bootmem_node_data[nid]; + unsigned long start_pfn = bdata->node_boot_start >> PAGE_SHIFT; + unsigned long end_pfn = bdata->node_low_pfn; if (dma_local_pfn >= end_pfn - start_pfn) zones_size[ZONE_DMA] = end_pfn - start_pfn; diff --git a/arch/arm/mm/discontig.c b/arch/arm/mm/discontig.c index 1e5602189507..c8c0c4b0f0a3 100644 --- a/arch/arm/mm/discontig.c +++ b/arch/arm/mm/discontig.c @@ -21,26 +21,24 @@ * Our node_data structure for discontiguous memory. */ -static bootmem_data_t node_bootmem_data[MAX_NUMNODES]; - pg_data_t discontig_node_data[MAX_NUMNODES] = { - { .bdata = &node_bootmem_data[0] }, - { .bdata = &node_bootmem_data[1] }, - { .bdata = &node_bootmem_data[2] }, - { .bdata = &node_bootmem_data[3] }, + { .bdata = &bootmem_node_data[0] }, + { .bdata = &bootmem_node_data[1] }, + { .bdata = &bootmem_node_data[2] }, + { .bdata = &bootmem_node_data[3] }, #if MAX_NUMNODES == 16 - { .bdata = &node_bootmem_data[4] }, - { .bdata = &node_bootmem_data[5] }, - { .bdata = &node_bootmem_data[6] }, - { .bdata = &node_bootmem_data[7] }, - { .bdata = &node_bootmem_data[8] }, - { .bdata = &node_bootmem_data[9] }, - { .bdata = &node_bootmem_data[10] }, - { .bdata = &node_bootmem_data[11] }, - { .bdata = &node_bootmem_data[12] }, - { .bdata = &node_bootmem_data[13] }, - { .bdata = &node_bootmem_data[14] }, - { .bdata = &node_bootmem_data[15] }, + { .bdata = &bootmem_node_data[4] }, + { .bdata = &bootmem_node_data[5] }, + { .bdata = &bootmem_node_data[6] }, + { .bdata = &bootmem_node_data[7] }, + { .bdata = &bootmem_node_data[8] }, + { .bdata = &bootmem_node_data[9] }, + { .bdata = &bootmem_node_data[10] }, + { .bdata = &bootmem_node_data[11] }, + { .bdata = &bootmem_node_data[12] }, + { .bdata = &bootmem_node_data[13] }, + { .bdata = &bootmem_node_data[14] }, + { .bdata = &bootmem_node_data[15] }, #endif }; diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 544dc420c65e..2fcf8464331e 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -36,7 +36,6 @@ struct early_node_data { struct ia64_node_data *node_data; unsigned long pernode_addr; unsigned long pernode_size; - struct bootmem_data bootmem_data; unsigned long num_physpages; #ifdef CONFIG_ZONE_DMA unsigned long num_dma_physpages; @@ -76,7 +75,7 @@ static int __init build_node_maps(unsigned long start, unsigned long len, int node) { unsigned long cstart, epfn, end = start + len; - struct bootmem_data *bdp = &mem_data[node].bootmem_data; + struct bootmem_data *bdp = &bootmem_node_data[node]; epfn = GRANULEROUNDUP(end) >> PAGE_SHIFT; cstart = GRANULEROUNDDOWN(start); @@ -167,7 +166,7 @@ static void __init fill_pernode(int node, unsigned long pernode, { void *cpu_data; int cpus = early_nr_cpus_node(node); - struct bootmem_data *bdp = &mem_data[node].bootmem_data; + struct bootmem_data *bdp = &bootmem_node_data[node]; mem_data[node].pernode_addr = pernode; mem_data[node].pernode_size = pernodesize; @@ -224,7 +223,7 @@ static int __init find_pernode_space(unsigned long start, unsigned long len, { unsigned long epfn; unsigned long pernodesize = 0, pernode, pages, mapsize; - struct bootmem_data *bdp = &mem_data[node].bootmem_data; + struct bootmem_data *bdp = &bootmem_node_data[node]; epfn = (start + len) >> PAGE_SHIFT; @@ -440,7 +439,7 @@ void __init find_memory(void) efi_memmap_walk(find_max_min_low_pfn, NULL); for_each_online_node(node) - if (mem_data[node].bootmem_data.node_low_pfn) { + if (bootmem_node_data[node].node_low_pfn) { node_clear(node, memory_less_mask); mem_data[node].min_pfn = ~0UL; } @@ -460,7 +459,7 @@ void __init find_memory(void) else if (node_isset(node, memory_less_mask)) continue; - bdp = &mem_data[node].bootmem_data; + bdp = &bootmem_node_data[node]; pernode = mem_data[node].pernode_addr; pernodesize = mem_data[node].pernode_size; map = pernode + pernodesize; diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c index 07c1af7dc0e2..aa9145ef6cca 100644 --- a/arch/m32r/mm/discontig.c +++ b/arch/m32r/mm/discontig.c @@ -20,7 +20,6 @@ extern char _end[]; struct pglist_data *node_data[MAX_NUMNODES]; EXPORT_SYMBOL(node_data); -static bootmem_data_t node_bdata[MAX_NUMNODES] __initdata; pg_data_t m32r_node_data[MAX_NUMNODES]; @@ -81,7 +80,7 @@ unsigned long __init setup_memory(void) for_each_online_node(nid) { mp = &mem_prof[nid]; NODE_DATA(nid)=(pg_data_t *)&m32r_node_data[nid]; - NODE_DATA(nid)->bdata = &node_bdata[nid]; + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; min_pfn = mp->start_pfn; max_pfn = mp->start_pfn + mp->pages; bootmap_size = init_bootmem_node(NODE_DATA(nid), mp->free_pfn, @@ -163,4 +162,3 @@ unsigned long __init zone_sizes_init(void) return holes; } - diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index d8fb9c5303cc..79f5f94d4800 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -32,8 +32,6 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); -static bootmem_data_t __initdata bootmem_data[MAX_NUMNODES]; - pg_data_t pg_data_map[MAX_NUMNODES]; EXPORT_SYMBOL(pg_data_map); @@ -58,7 +56,7 @@ void __init m68k_setup_node(int node) pg_data_table[i] = pg_data_map + node; } #endif - pg_data_map[node].bdata = bootmem_data + node; + pg_data_map[node].bdata = bootmem_node_data + node; node_set_online(node); } diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index 42cd10956306..060d853d7b35 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -33,8 +33,6 @@ #define SLOT_PFNSHIFT (SLOT_SHIFT - PAGE_SHIFT) #define PFN_NASIDSHFT (NASID_SHFT - PAGE_SHIFT) -static struct bootmem_data __initdata plat_node_bdata[MAX_COMPACT_NODES]; - struct node_data *__node_data[MAX_COMPACT_NODES]; EXPORT_SYMBOL(__node_data); @@ -403,7 +401,7 @@ static void __init node_mem_init(cnodeid_t node) */ __node_data[node] = __va(slot_freepfn << PAGE_SHIFT); - NODE_DATA(node)->bdata = &plat_node_bdata[node]; + NODE_DATA(node)->bdata = &bootmem_node_data[node]; NODE_DATA(node)->node_start_pfn = start_pfn; NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn; diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index b4d6c8777ed0..0ddf4904640a 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -36,7 +36,6 @@ extern int data_start; #ifdef CONFIG_DISCONTIGMEM struct node_map_data node_data[MAX_NUMNODES] __read_mostly; -bootmem_data_t bmem_data[MAX_NUMNODES] __read_mostly; unsigned char pfnnid_map[PFNNID_MAP_MAX] __read_mostly; #endif @@ -262,7 +261,7 @@ static void __init setup_bootmem(void) #ifdef CONFIG_DISCONTIGMEM for (i = 0; i < MAX_PHYSMEM_RANGES; i++) { memset(NODE_DATA(i), 0, sizeof(pg_data_t)); - NODE_DATA(i)->bdata = &bmem_data[i]; + NODE_DATA(i)->bdata = &bootmem_node_data[i]; } memset(pfnnid_map, 0xff, sizeof(pfnnid_map)); diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index cf4bffba6f7c..d9a181351332 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -39,7 +39,6 @@ EXPORT_SYMBOL(numa_cpu_lookup_table); EXPORT_SYMBOL(numa_cpumask_lookup_table); EXPORT_SYMBOL(node_data); -static bootmem_data_t __initdata plat_node_bdata[MAX_NUMNODES]; static int min_common_depth; static int n_mem_addr_cells, n_mem_size_cells; @@ -816,7 +815,7 @@ void __init do_init_bootmem(void) dbg("node %d\n", nid); dbg("NODE_DATA() = %p\n", NODE_DATA(nid)); - NODE_DATA(nid)->bdata = &plat_node_bdata[nid]; + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; NODE_DATA(nid)->node_start_pfn = start_pfn; NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn; diff --git a/arch/sh/mm/numa.c b/arch/sh/mm/numa.c index 1663199ce888..095d93bec7cd 100644 --- a/arch/sh/mm/numa.c +++ b/arch/sh/mm/numa.c @@ -14,7 +14,6 @@ #include #include -static bootmem_data_t plat_node_bdata[MAX_NUMNODES]; struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; EXPORT_SYMBOL_GPL(node_data); @@ -35,7 +34,7 @@ void __init setup_memory(void) NODE_DATA(0) = pfn_to_kaddr(free_pfn); memset(NODE_DATA(0), 0, sizeof(struct pglist_data)); free_pfn += PFN_UP(sizeof(struct pglist_data)); - NODE_DATA(0)->bdata = &plat_node_bdata[0]; + NODE_DATA(0)->bdata = &bootmem_node_data[0]; /* Set up node 0 */ setup_bootmem_allocator(free_pfn); @@ -66,7 +65,7 @@ void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end) free_pfn += PFN_UP(sizeof(struct pglist_data)); memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); - NODE_DATA(nid)->bdata = &plat_node_bdata[nid]; + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; NODE_DATA(nid)->node_start_pfn = start_pfn; NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn; diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 84898c44dd4d..713297473951 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -788,7 +788,6 @@ int numa_cpu_lookup_table[NR_CPUS]; cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES]; #ifdef CONFIG_NEED_MULTIPLE_NODES -static bootmem_data_t plat_node_bdata[MAX_NUMNODES]; struct mdesc_mblock { u64 base; @@ -871,7 +870,7 @@ static void __init allocate_node_data(int nid) NODE_DATA(nid) = __va(paddr); memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); - NODE_DATA(nid)->bdata = &plat_node_bdata[nid]; + NODE_DATA(nid)->bdata = &bootmem_node_data[nid]; #endif p = NODE_DATA(nid); diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/discontig_32.c index 5dfef9fa061a..62fa440678d8 100644 --- a/arch/x86/mm/discontig_32.c +++ b/arch/x86/mm/discontig_32.c @@ -42,7 +42,6 @@ struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; EXPORT_SYMBOL(node_data); -static bootmem_data_t node0_bdata; /* * numa interface - we expect the numa architecture specific code to have @@ -385,7 +384,7 @@ void __init initmem_init(unsigned long start_pfn, for_each_online_node(nid) memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); - NODE_DATA(0)->bdata = &node0_bdata; + NODE_DATA(0)->bdata = &bootmem_node_data[0]; setup_bootmem_allocator(); } diff --git a/arch/x86/mm/numa_64.c b/arch/x86/mm/numa_64.c index 9782f42dd319..a4dd793d6003 100644 --- a/arch/x86/mm/numa_64.c +++ b/arch/x86/mm/numa_64.c @@ -23,8 +23,6 @@ struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; EXPORT_SYMBOL(node_data); -static bootmem_data_t plat_node_bdata[MAX_NUMNODES]; - struct memnode memnode; s16 apicid_to_node[MAX_LOCAL_APIC] __cpuinitdata = { @@ -198,7 +196,7 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, nodedata_phys + pgdat_size - 1); memset(NODE_DATA(nodeid), 0, sizeof(pg_data_t)); - NODE_DATA(nodeid)->bdata = &plat_node_bdata[nodeid]; + NODE_DATA(nodeid)->bdata = &bootmem_node_data[nodeid]; NODE_DATA(nodeid)->node_start_pfn = start_pfn; NODE_DATA(nodeid)->node_spanned_pages = last_pfn - start_pfn; diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index a1d9b79078ea..2599c741405e 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -38,6 +38,8 @@ typedef struct bootmem_data { struct list_head list; } bootmem_data_t; +extern bootmem_data_t bootmem_node_data[]; + extern unsigned long bootmem_bootmap_pages(unsigned long); extern unsigned long init_bootmem(unsigned long addr, unsigned long memend); extern void free_bootmem(unsigned long addr, unsigned long size); diff --git a/mm/bootmem.c b/mm/bootmem.c index 9f4bbc5da73f..35b3cb667036 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -36,6 +36,8 @@ static LIST_HEAD(bdata_list); unsigned long saved_max_pfn; #endif +bootmem_data_t bootmem_node_data[MAX_NUMNODES] __initdata; + /* return the number of _pages_ that will be allocated for the boot bitmap */ unsigned long __init bootmem_bootmap_pages(unsigned long pages) { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 9ece07ce65b0..e089b92cdfff 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4040,9 +4040,7 @@ void __init set_dma_reserve(unsigned long new_dma_reserve) } #ifndef CONFIG_NEED_MULTIPLE_NODES -static bootmem_data_t contig_bootmem_data; -struct pglist_data contig_page_data = { .bdata = &contig_bootmem_data }; - +struct pglist_data contig_page_data = { .bdata = &bootmem_node_data[0] }; EXPORT_SYMBOL(contig_page_data); #endif -- cgit From ffc6421f0720f433b5b35b89ff56e998eabff93b Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:26:59 -0700 Subject: mm: unexport __alloc_bootmem_core() This function has no external callers, so unexport it. Also fix its naming inconsistency. Signed-off-by: Johannes Weiner Cc: Ingo Molnar Cc: Yinghai Lu Cc: Christoph Lameter Cc: Mel Gorman Cc: Andy Whitcroft Cc: Mel Gorman Cc: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 5 ----- mm/bootmem.c | 24 ++++++++++++------------ 2 files changed, 12 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 2599c741405e..dd8fee6c46d9 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -56,11 +56,6 @@ extern void *__alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal); -extern void *__alloc_bootmem_core(struct bootmem_data *bdata, - unsigned long size, - unsigned long align, - unsigned long goal, - unsigned long limit); /* * flags for reserve_bootmem (also if CONFIG_HAVE_ARCH_BOOTMEM_NODE, diff --git a/mm/bootmem.c b/mm/bootmem.c index 251c66c5d96a..4bc6ae2fbaa3 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -234,9 +234,9 @@ static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, * * NOTE: This function is _not_ reentrant. */ -void * __init -__alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, - unsigned long align, unsigned long goal, unsigned long limit) +static void * __init +alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, + unsigned long align, unsigned long goal, unsigned long limit) { unsigned long areasize, preferred; unsigned long i, start = 0, incr, eidx, end_pfn; @@ -245,7 +245,7 @@ __alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, void *node_bootmem_map; if (!size) { - printk("__alloc_bootmem_core(): zero-sized request\n"); + printk("alloc_bootmem_core(): zero-sized request\n"); BUG(); } BUG_ON(align & (align-1)); @@ -512,7 +512,7 @@ void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align, void *ptr; list_for_each_entry(bdata, &bdata_list, list) { - ptr = __alloc_bootmem_core(bdata, size, align, goal, 0); + ptr = alloc_bootmem_core(bdata, size, align, goal, 0); if (ptr) return ptr; } @@ -540,7 +540,7 @@ void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, { void *ptr; - ptr = __alloc_bootmem_core(pgdat->bdata, size, align, goal, 0); + ptr = alloc_bootmem_core(pgdat->bdata, size, align, goal, 0); if (ptr) return ptr; @@ -559,8 +559,8 @@ void * __init alloc_bootmem_section(unsigned long size, goal = PFN_PHYS(pfn); limit = PFN_PHYS(section_nr_to_pfn(section_nr + 1)) - 1; pgdat = NODE_DATA(early_pfn_to_nid(pfn)); - ptr = __alloc_bootmem_core(pgdat->bdata, size, SMP_CACHE_BYTES, goal, - limit); + ptr = alloc_bootmem_core(pgdat->bdata, size, SMP_CACHE_BYTES, goal, + limit); if (!ptr) return NULL; @@ -589,8 +589,8 @@ void * __init __alloc_bootmem_low(unsigned long size, unsigned long align, void *ptr; list_for_each_entry(bdata, &bdata_list, list) { - ptr = __alloc_bootmem_core(bdata, size, align, goal, - ARCH_LOW_ADDRESS_LIMIT); + ptr = alloc_bootmem_core(bdata, size, align, goal, + ARCH_LOW_ADDRESS_LIMIT); if (ptr) return ptr; } @@ -606,6 +606,6 @@ void * __init __alloc_bootmem_low(unsigned long size, unsigned long align, void * __init __alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal) { - return __alloc_bootmem_core(pgdat->bdata, size, align, goal, - ARCH_LOW_ADDRESS_LIMIT); + return alloc_bootmem_core(pgdat->bdata, size, align, goal, + ARCH_LOW_ADDRESS_LIMIT); } -- cgit From e4048e5dc4aecec670f48ed007a28779f09cebd6 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Wed, 23 Jul 2008 21:27:01 -0700 Subject: page allocator: inline some __alloc_pages() wrappers Two zonelist patch series rewrote __page_alloc() largely. Now, it is just a wrapper function. Inlining them will save a function call. [akpm@linux-foundation.org: export __alloc_pages_internal] Cc: Lee Schermerhorn Cc: Mel Gorman Signed-off-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 21 +++++++++++++++++---- mm/page_alloc.c | 19 ++----------------- 2 files changed, 19 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index b414be387180..f640ed241422 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -173,11 +173,24 @@ static inline void arch_free_page(struct page *page, int order) { } static inline void arch_alloc_page(struct page *page, int order) { } #endif -extern struct page *__alloc_pages(gfp_t, unsigned int, struct zonelist *); +struct page * +__alloc_pages_internal(gfp_t gfp_mask, unsigned int order, + struct zonelist *zonelist, nodemask_t *nodemask); + +static inline struct page * +__alloc_pages(gfp_t gfp_mask, unsigned int order, + struct zonelist *zonelist) +{ + return __alloc_pages_internal(gfp_mask, order, zonelist, NULL); +} + +static inline struct page * +__alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, + struct zonelist *zonelist, nodemask_t *nodemask) +{ + return __alloc_pages_internal(gfp_mask, order, zonelist, nodemask); +} -extern struct page * -__alloc_pages_nodemask(gfp_t, unsigned int, - struct zonelist *, nodemask_t *nodemask); static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask, unsigned int order) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index e089b92cdfff..35b1347d81bb 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1429,7 +1429,7 @@ try_next_zone: /* * This is the 'heart' of the zoned buddy allocator. */ -static struct page * +struct page * __alloc_pages_internal(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, nodemask_t *nodemask) { @@ -1632,22 +1632,7 @@ nopage: got_pg: return page; } - -struct page * -__alloc_pages(gfp_t gfp_mask, unsigned int order, - struct zonelist *zonelist) -{ - return __alloc_pages_internal(gfp_mask, order, zonelist, NULL); -} - -struct page * -__alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, - struct zonelist *zonelist, nodemask_t *nodemask) -{ - return __alloc_pages_internal(gfp_mask, order, zonelist, nodemask); -} - -EXPORT_SYMBOL(__alloc_pages); +EXPORT_SYMBOL(__alloc_pages_internal); /* * Common helper functions. -- cgit From c748e1340e0de3fa7fed86f8bdf499be9242afff Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 23 Jul 2008 21:27:03 -0700 Subject: mm/vmstat.c: proper externs This patch adds proper extern declarations for five variables in include/linux/vmstat.h Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/proc_misc.c | 4 ---- include/linux/vmstat.h | 6 ++++++ kernel/sysctl.c | 2 +- mm/vmstat.c | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index c652d469dc08..b14f43d25e9e 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -232,7 +232,6 @@ static int meminfo_read_proc(char *page, char **start, off_t off, #undef K } -extern const struct seq_operations fragmentation_op; static int fragmentation_open(struct inode *inode, struct file *file) { (void)inode; @@ -246,7 +245,6 @@ static const struct file_operations fragmentation_file_operations = { .release = seq_release, }; -extern const struct seq_operations pagetypeinfo_op; static int pagetypeinfo_open(struct inode *inode, struct file *file) { return seq_open(file, &pagetypeinfo_op); @@ -259,7 +257,6 @@ static const struct file_operations pagetypeinfo_file_ops = { .release = seq_release, }; -extern const struct seq_operations zoneinfo_op; static int zoneinfo_open(struct inode *inode, struct file *file) { return seq_open(file, &zoneinfo_op); @@ -356,7 +353,6 @@ static const struct file_operations proc_devinfo_operations = { .release = seq_release, }; -extern const struct seq_operations vmstat_op; static int vmstat_open(struct inode *inode, struct file *file) { return seq_open(file, &vmstat_op); diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h index e83b69346d23..58334d439516 100644 --- a/include/linux/vmstat.h +++ b/include/linux/vmstat.h @@ -44,6 +44,12 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT, NR_VM_EVENT_ITEMS }; +extern const struct seq_operations fragmentation_op; +extern const struct seq_operations pagetypeinfo_op; +extern const struct seq_operations zoneinfo_op; +extern const struct seq_operations vmstat_op; +extern int sysctl_stat_interval; + #ifdef CONFIG_VM_EVENT_COUNTERS /* * Light weight per cpu counter implementation. diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 2a7b9d88706b..1f7b3b76a166 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -80,7 +81,6 @@ extern int sysctl_drop_caches; extern int percpu_pagelist_fraction; extern int compat_log; extern int maps_protect; -extern int sysctl_stat_interval; extern int latencytop_enabled; extern int sysctl_nr_open_min, sysctl_nr_open_max; #ifdef CONFIG_RCU_TORTURE_TEST diff --git a/mm/vmstat.c b/mm/vmstat.c index c3d4a781802f..b0d08e667ece 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #ifdef CONFIG_VM_EVENT_COUNTERS -- cgit From 0d71d10a4252a3938e6b70189bc776171c02e076 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Wed, 23 Jul 2008 21:27:05 -0700 Subject: mm: remove nopfn There are no users of nopfn in the tree. Remove it. [hugh@veritas.com: fix build error] Signed-off-by: Nick Piggin Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 9 -------- mm/memory.c | 67 ++++++------------------------------------------------ 2 files changed, 7 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 2128ef7780c6..eb815cfc1b35 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -166,8 +166,6 @@ struct vm_operations_struct { void (*open)(struct vm_area_struct * area); void (*close)(struct vm_area_struct * area); int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf); - unsigned long (*nopfn)(struct vm_area_struct *area, - unsigned long address); /* notification that a previously read-only page is about to become * writable, if an error is returned it will cause a SIGBUS */ @@ -674,13 +672,6 @@ static inline int page_mapped(struct page *page) return atomic_read(&(page)->_mapcount) >= 0; } -/* - * Error return values for the *_nopfn functions - */ -#define NOPFN_SIGBUS ((unsigned long) -1) -#define NOPFN_OOM ((unsigned long) -2) -#define NOPFN_REFAULT ((unsigned long) -3) - /* * Different kinds of faults, as returned by handle_mm_fault(). * Used to decide whether a process gets delivered SIGBUS or diff --git a/mm/memory.c b/mm/memory.c index 2302d228fe04..46dbed4b7446 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1058,11 +1058,9 @@ static inline int use_zero_page(struct vm_area_struct *vma) if (vma->vm_flags & (VM_LOCKED | VM_SHARED)) return 0; /* - * And if we have a fault or a nopfn routine, it's not an - * anonymous region. + * And if we have a fault routine, it's not an anonymous region. */ - return !vma->vm_ops || - (!vma->vm_ops->fault && !vma->vm_ops->nopfn); + return !vma->vm_ops || !vma->vm_ops->fault; } int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, @@ -1338,6 +1336,11 @@ out: * * This function should only be called from a vm_ops->fault handler, and * in that case the handler should return NULL. + * + * vma cannot be a COW mapping. + * + * As this is called only for pages that do not currently exist, we + * do not need to flush old virtual caches or the TLB. */ int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn) @@ -2501,59 +2504,6 @@ static int do_linear_fault(struct mm_struct *mm, struct vm_area_struct *vma, return __do_fault(mm, vma, address, pmd, pgoff, flags, orig_pte); } - -/* - * do_no_pfn() tries to create a new page mapping for a page without - * a struct_page backing it - * - * As this is called only for pages that do not currently exist, we - * do not need to flush old virtual caches or the TLB. - * - * We enter with non-exclusive mmap_sem (to exclude vma changes, - * but allow concurrent faults), and pte mapped but not yet locked. - * We return with mmap_sem still held, but pte unmapped and unlocked. - * - * It is expected that the ->nopfn handler always returns the same pfn - * for a given virtual mapping. - * - * Mark this `noinline' to prevent it from bloating the main pagefault code. - */ -static noinline int do_no_pfn(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long address, pte_t *page_table, pmd_t *pmd, - int write_access) -{ - spinlock_t *ptl; - pte_t entry; - unsigned long pfn; - - pte_unmap(page_table); - BUG_ON(!(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP))); - BUG_ON((vma->vm_flags & VM_PFNMAP) && is_cow_mapping(vma->vm_flags)); - - pfn = vma->vm_ops->nopfn(vma, address & PAGE_MASK); - - BUG_ON((vma->vm_flags & VM_MIXEDMAP) && pfn_valid(pfn)); - - if (unlikely(pfn == NOPFN_OOM)) - return VM_FAULT_OOM; - else if (unlikely(pfn == NOPFN_SIGBUS)) - return VM_FAULT_SIGBUS; - else if (unlikely(pfn == NOPFN_REFAULT)) - return 0; - - page_table = pte_offset_map_lock(mm, pmd, address, &ptl); - - /* Only go through if we didn't race with anybody else... */ - if (pte_none(*page_table)) { - entry = pfn_pte(pfn, vma->vm_page_prot); - if (write_access) - entry = maybe_mkwrite(pte_mkdirty(entry), vma); - set_pte_at(mm, address, page_table, entry); - } - pte_unmap_unlock(page_table, ptl); - return 0; -} - /* * Fault of a previously existing named mapping. Repopulate the pte * from the encoded file_pte if possible. This enables swappable @@ -2614,9 +2564,6 @@ static inline int handle_pte_fault(struct mm_struct *mm, if (likely(vma->vm_ops->fault)) return do_linear_fault(mm, vma, address, pte, pmd, write_access, entry); - if (unlikely(vma->vm_ops->nopfn)) - return do_no_pfn(mm, vma, address, pte, - pmd, write_access); } return do_anonymous_page(mm, vma, address, pte, pmd, write_access); -- cgit From 28b2ee20c7cba812b6f2ccf6d722cf86d00a84dc Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Wed, 23 Jul 2008 21:27:05 -0700 Subject: access_process_vm device memory infrastructure In order to be able to debug things like the X server and programs using the PPC Cell SPUs, the debugger needs to be able to access device memory through ptrace and /proc/pid/mem. This patch: Add the generic_access_phys access function and put the hooks in place to allow access_process_vm to access device or PPC Cell SPU memory. [riel@redhat.com: Add documentation for the vm_ops->access function] Signed-off-by: Rik van Riel Signed-off-by: Benjamin Herrensmidt Cc: Dave Airlie Cc: Hugh Dickins Cc: Paul Mackerras Cc: Arnd Bergmann Acked-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 7 ++ arch/Kconfig | 3 + arch/x86/Kconfig | 1 + arch/x86/mm/ioremap.c | 8 +++ include/asm-x86/io_32.h | 2 + include/asm-x86/io_64.h | 2 + include/linux/mm.h | 8 +++ mm/memory.c | 131 ++++++++++++++++++++++++++++++++------ 8 files changed, 144 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 8b22d7d8b991..680fb566b928 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -510,6 +510,7 @@ prototypes: void (*close)(struct vm_area_struct*); int (*fault)(struct vm_area_struct*, struct vm_fault *); int (*page_mkwrite)(struct vm_area_struct *, struct page *); + int (*access)(struct vm_area_struct *, unsigned long, void*, int, int); locking rules: BKL mmap_sem PageLocked(page) @@ -517,6 +518,7 @@ open: no yes close: no yes fault: no yes page_mkwrite: no yes no +access: no yes ->page_mkwrite() is called when a previously read-only page is about to become writeable. The file system is responsible for @@ -525,6 +527,11 @@ taking to lock out truncate, the page range should be verified to be within i_size. The page mapping should also be checked that it is not NULL. + ->access() is called when get_user_pages() fails in +acces_process_vm(), typically used to debug a process through +/proc/pid/mem or ptrace. This function is needed only for +VM_IO | VM_PFNMAP VMAs. + ================================================================================ Dubious stuff diff --git a/arch/Kconfig b/arch/Kconfig index 4d5ebbc1e72b..6093c0be58b0 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -31,6 +31,9 @@ config KRETPROBES def_bool y depends on KPROBES && HAVE_KRETPROBES +config HAVE_IOREMAP_PROT + def_bool n + config HAVE_KPROBES def_bool n diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 03980cb04291..b2ddfcf01728 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -21,6 +21,7 @@ config X86 select HAVE_UNSTABLE_SCHED_CLOCK select HAVE_IDE select HAVE_OPROFILE + select HAVE_IOREMAP_PROT select HAVE_KPROBES select HAVE_KRETPROBES select HAVE_DYNAMIC_FTRACE diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index 24c1d3c30186..016f335bbeea 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -330,6 +330,14 @@ static void __iomem *ioremap_default(resource_size_t phys_addr, return (void __iomem *)ret; } +void __iomem *ioremap_prot(resource_size_t phys_addr, unsigned long size, + unsigned long prot_val) +{ + return __ioremap_caller(phys_addr, size, (prot_val & _PAGE_CACHE_MASK), + __builtin_return_address(0)); +} +EXPORT_SYMBOL(ioremap_prot); + /** * iounmap - Free a IO remapping * @addr: virtual address from ioremap_* diff --git a/include/asm-x86/io_32.h b/include/asm-x86/io_32.h index 4df44ed54077..e876d89ac156 100644 --- a/include/asm-x86/io_32.h +++ b/include/asm-x86/io_32.h @@ -110,6 +110,8 @@ static inline void *phys_to_virt(unsigned long address) */ extern void __iomem *ioremap_nocache(resource_size_t offset, unsigned long size); extern void __iomem *ioremap_cache(resource_size_t offset, unsigned long size); +extern void __iomem *ioremap_prot(resource_size_t offset, unsigned long size, + unsigned long prot_val); /* * The default ioremap() behavior is non-cached: diff --git a/include/asm-x86/io_64.h b/include/asm-x86/io_64.h index ddd8058a5026..22995c5c5adc 100644 --- a/include/asm-x86/io_64.h +++ b/include/asm-x86/io_64.h @@ -175,6 +175,8 @@ extern void early_iounmap(void *addr, unsigned long size); */ extern void __iomem *ioremap_nocache(resource_size_t offset, unsigned long size); extern void __iomem *ioremap_cache(resource_size_t offset, unsigned long size); +extern void __iomem *ioremap_prot(resource_size_t offset, unsigned long size, + unsigned long prot_val); /* * The default ioremap() behavior is non-cached: diff --git a/include/linux/mm.h b/include/linux/mm.h index eb815cfc1b35..5c7f8f64f70e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -170,6 +170,12 @@ struct vm_operations_struct { /* notification that a previously read-only page is about to become * writable, if an error is returned it will cause a SIGBUS */ int (*page_mkwrite)(struct vm_area_struct *vma, struct page *page); + + /* called by access_process_vm when get_user_pages() fails, typically + * for use by special VMAs that can switch between memory and hardware + */ + int (*access)(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write); #ifdef CONFIG_NUMA /* * set_policy() op must add a reference to any non-NULL @new mempolicy @@ -771,6 +777,8 @@ int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma); void unmap_mapping_range(struct address_space *mapping, loff_t const holebegin, loff_t const holelen, int even_cows); +int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write); static inline void unmap_shared_mapping_range(struct address_space *mapping, loff_t const holebegin, loff_t const holelen) diff --git a/mm/memory.c b/mm/memory.c index 46dbed4b7446..87350321e66f 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2751,6 +2751,86 @@ int in_gate_area_no_task(unsigned long addr) #endif /* __HAVE_ARCH_GATE_AREA */ +#ifdef CONFIG_HAVE_IOREMAP_PROT +static resource_size_t follow_phys(struct vm_area_struct *vma, + unsigned long address, unsigned int flags, + unsigned long *prot) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ptep, pte; + spinlock_t *ptl; + resource_size_t phys_addr = 0; + struct mm_struct *mm = vma->vm_mm; + + VM_BUG_ON(!(vma->vm_flags & (VM_IO | VM_PFNMAP))); + + pgd = pgd_offset(mm, address); + if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) + goto no_page_table; + + pud = pud_offset(pgd, address); + if (pud_none(*pud) || unlikely(pud_bad(*pud))) + goto no_page_table; + + pmd = pmd_offset(pud, address); + if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) + goto no_page_table; + + /* We cannot handle huge page PFN maps. Luckily they don't exist. */ + if (pmd_huge(*pmd)) + goto no_page_table; + + ptep = pte_offset_map_lock(mm, pmd, address, &ptl); + if (!ptep) + goto out; + + pte = *ptep; + if (!pte_present(pte)) + goto unlock; + if ((flags & FOLL_WRITE) && !pte_write(pte)) + goto unlock; + phys_addr = pte_pfn(pte); + phys_addr <<= PAGE_SHIFT; /* Shift here to avoid overflow on PAE */ + + *prot = pgprot_val(pte_pgprot(pte)); + +unlock: + pte_unmap_unlock(ptep, ptl); +out: + return phys_addr; +no_page_table: + return 0; +} + +int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write) +{ + resource_size_t phys_addr; + unsigned long prot = 0; + void *maddr; + int offset = addr & (PAGE_SIZE-1); + + if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) + return -EINVAL; + + phys_addr = follow_phys(vma, addr, write, &prot); + + if (!phys_addr) + return -EINVAL; + + maddr = ioremap_prot(phys_addr, PAGE_SIZE, prot); + if (write) + memcpy_toio(maddr + offset, buf, len); + else + memcpy_fromio(buf, maddr + offset, len); + iounmap(maddr); + + return len; +} +#endif + /* * Access another process' address space. * Source/target buffer must be kernel space, @@ -2760,7 +2840,6 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in { struct mm_struct *mm; struct vm_area_struct *vma; - struct page *page; void *old_buf = buf; mm = get_task_mm(tsk); @@ -2772,28 +2851,44 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in while (len) { int bytes, ret, offset; void *maddr; + struct page *page = NULL; ret = get_user_pages(tsk, mm, addr, 1, write, 1, &page, &vma); - if (ret <= 0) - break; - - bytes = len; - offset = addr & (PAGE_SIZE-1); - if (bytes > PAGE_SIZE-offset) - bytes = PAGE_SIZE-offset; - - maddr = kmap(page); - if (write) { - copy_to_user_page(vma, page, addr, - maddr + offset, buf, bytes); - set_page_dirty_lock(page); + if (ret <= 0) { + /* + * Check if this is a VM_IO | VM_PFNMAP VMA, which + * we can access using slightly different code. + */ +#ifdef CONFIG_HAVE_IOREMAP_PROT + vma = find_vma(mm, addr); + if (!vma) + break; + if (vma->vm_ops && vma->vm_ops->access) + ret = vma->vm_ops->access(vma, addr, buf, + len, write); + if (ret <= 0) +#endif + break; + bytes = ret; } else { - copy_from_user_page(vma, page, addr, - buf, maddr + offset, bytes); + bytes = len; + offset = addr & (PAGE_SIZE-1); + if (bytes > PAGE_SIZE-offset) + bytes = PAGE_SIZE-offset; + + maddr = kmap(page); + if (write) { + copy_to_user_page(vma, page, addr, + maddr + offset, buf, bytes); + set_page_dirty_lock(page); + } else { + copy_from_user_page(vma, page, addr, + buf, maddr + offset, bytes); + } + kunmap(page); + page_cache_release(page); } - kunmap(page); - page_cache_release(page); len -= bytes; buf += bytes; addr += bytes; -- cgit From 42b7772812d15b86543a23b82bd6070eef9a08b1 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Wed, 23 Jul 2008 21:27:10 -0700 Subject: mm: remove double indirection on tlb parameter to free_pgd_range() & Co The double indirection here is not needed anywhere and hence (at least) confusing. Signed-off-by: Jan Beulich Cc: Hugh Dickins Cc: Nick Piggin Cc: Christoph Lameter Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: "Luck, Tony" Cc: Paul Mundt Cc: "David S. Miller" Acked-by: Jeremy Fitzhardinge Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/mm/hugetlbpage.c | 2 +- arch/powerpc/mm/hugetlbpage.c | 8 ++++---- fs/exec.c | 4 ++-- include/asm-ia64/hugetlb.h | 2 +- include/asm-powerpc/hugetlb.h | 2 +- include/asm-sh/hugetlb.h | 2 +- include/asm-sparc/hugetlb.h | 2 +- include/asm-x86/hugetlb.h | 2 +- include/linux/mm.h | 4 +--- mm/internal.h | 3 +++ mm/memory.c | 10 ++++++---- mm/mmap.c | 6 ++++-- 12 files changed, 26 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/mm/hugetlbpage.c b/arch/ia64/mm/hugetlbpage.c index d3ce8f3bcaa6..cd49e2860eef 100644 --- a/arch/ia64/mm/hugetlbpage.c +++ b/arch/ia64/mm/hugetlbpage.c @@ -112,7 +112,7 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int wri return NULL; } -void hugetlb_free_pgd_range(struct mmu_gather **tlb, +void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) { diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 0d12fba31bc5..1a96cc891cf5 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -255,7 +255,7 @@ static void hugetlb_free_pud_range(struct mmu_gather *tlb, pgd_t *pgd, * * Must be called with pagetable lock held. */ -void hugetlb_free_pgd_range(struct mmu_gather **tlb, +void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) { @@ -315,13 +315,13 @@ void hugetlb_free_pgd_range(struct mmu_gather **tlb, return; start = addr; - pgd = pgd_offset((*tlb)->mm, addr); + pgd = pgd_offset(tlb->mm, addr); do { - BUG_ON(get_slice_psize((*tlb)->mm, addr) != mmu_huge_psize); + BUG_ON(get_slice_psize(tlb->mm, addr) != mmu_huge_psize); next = pgd_addr_end(addr, end); if (pgd_none_or_clear_bad(pgd)) continue; - hugetlb_free_pud_range(*tlb, pgd, addr, next, floor, ceiling); + hugetlb_free_pud_range(tlb, pgd, addr, next, floor, ceiling); } while (pgd++, addr = next, addr != end); } diff --git a/fs/exec.c b/fs/exec.c index fd9234379e8d..190ed1f92774 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -541,7 +541,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift) /* * when the old and new regions overlap clear from new_end. */ - free_pgd_range(&tlb, new_end, old_end, new_end, + free_pgd_range(tlb, new_end, old_end, new_end, vma->vm_next ? vma->vm_next->vm_start : 0); } else { /* @@ -550,7 +550,7 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift) * have constraints on va-space that make this illegal (IA64) - * for the others its just a little faster. */ - free_pgd_range(&tlb, old_start, old_end, new_end, + free_pgd_range(tlb, old_start, old_end, new_end, vma->vm_next ? vma->vm_next->vm_start : 0); } tlb_finish_mmu(tlb, new_end, old_end); diff --git a/include/asm-ia64/hugetlb.h b/include/asm-ia64/hugetlb.h index f28a9701f1cf..e9d1e5e2382d 100644 --- a/include/asm-ia64/hugetlb.h +++ b/include/asm-ia64/hugetlb.h @@ -4,7 +4,7 @@ #include -void hugetlb_free_pgd_range(struct mmu_gather **tlb, unsigned long addr, +void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling); diff --git a/include/asm-powerpc/hugetlb.h b/include/asm-powerpc/hugetlb.h index be32ff02f4a0..0a37aa5ecaa5 100644 --- a/include/asm-powerpc/hugetlb.h +++ b/include/asm-powerpc/hugetlb.h @@ -7,7 +7,7 @@ int is_hugepage_only_range(struct mm_struct *mm, unsigned long addr, unsigned long len); -void hugetlb_free_pgd_range(struct mmu_gather **tlb, unsigned long addr, +void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling); diff --git a/include/asm-sh/hugetlb.h b/include/asm-sh/hugetlb.h index 02402303d89b..fb30018938c7 100644 --- a/include/asm-sh/hugetlb.h +++ b/include/asm-sh/hugetlb.h @@ -26,7 +26,7 @@ static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) static inline void hugetlb_prefault_arch_hook(struct mm_struct *mm) { } -static inline void hugetlb_free_pgd_range(struct mmu_gather **tlb, +static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) diff --git a/include/asm-sparc/hugetlb.h b/include/asm-sparc/hugetlb.h index 412af58926a0..aeb92374ca3d 100644 --- a/include/asm-sparc/hugetlb.h +++ b/include/asm-sparc/hugetlb.h @@ -31,7 +31,7 @@ static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) return 0; } -static inline void hugetlb_free_pgd_range(struct mmu_gather **tlb, +static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) diff --git a/include/asm-x86/hugetlb.h b/include/asm-x86/hugetlb.h index 14171a4924f6..7eed6e0883bf 100644 --- a/include/asm-x86/hugetlb.h +++ b/include/asm-x86/hugetlb.h @@ -26,7 +26,7 @@ static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) static inline void hugetlb_prefault_arch_hook(struct mm_struct *mm) { } -static inline void hugetlb_free_pgd_range(struct mmu_gather **tlb, +static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) diff --git a/include/linux/mm.h b/include/linux/mm.h index 5c7f8f64f70e..f8071097302a 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -769,10 +769,8 @@ struct mm_walk { int walk_page_range(unsigned long addr, unsigned long end, struct mm_walk *walk); -void free_pgd_range(struct mmu_gather **tlb, unsigned long addr, +void free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling); -void free_pgtables(struct mmu_gather **tlb, struct vm_area_struct *start_vma, - unsigned long floor, unsigned long ceiling); int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma); void unmap_mapping_range(struct address_space *mapping, diff --git a/mm/internal.h b/mm/internal.h index 50807e12490e..858ad01864dc 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -13,6 +13,9 @@ #include +void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *start_vma, + unsigned long floor, unsigned long ceiling); + static inline void set_page_count(struct page *page, int v) { atomic_set(&page->_count, v); diff --git a/mm/memory.c b/mm/memory.c index 87350321e66f..82f3f1c5cf17 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -61,6 +61,8 @@ #include #include +#include "internal.h" + #ifndef CONFIG_NEED_MULTIPLE_NODES /* use the per-pgdat data instead for discontigmem - mbligh */ unsigned long max_mapnr; @@ -211,7 +213,7 @@ static inline void free_pud_range(struct mmu_gather *tlb, pgd_t *pgd, * * Must be called with pagetable lock held. */ -void free_pgd_range(struct mmu_gather **tlb, +void free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) { @@ -262,16 +264,16 @@ void free_pgd_range(struct mmu_gather **tlb, return; start = addr; - pgd = pgd_offset((*tlb)->mm, addr); + pgd = pgd_offset(tlb->mm, addr); do { next = pgd_addr_end(addr, end); if (pgd_none_or_clear_bad(pgd)) continue; - free_pud_range(*tlb, pgd, addr, next, floor, ceiling); + free_pud_range(tlb, pgd, addr, next, floor, ceiling); } while (pgd++, addr = next, addr != end); } -void free_pgtables(struct mmu_gather **tlb, struct vm_area_struct *vma, +void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *vma, unsigned long floor, unsigned long ceiling) { while (vma) { diff --git a/mm/mmap.c b/mm/mmap.c index 1d102b956fd8..75e0d0673d78 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -32,6 +32,8 @@ #include #include +#include "internal.h" + #ifndef arch_mmap_check #define arch_mmap_check(addr, len, flags) (0) #endif @@ -1763,7 +1765,7 @@ static void unmap_region(struct mm_struct *mm, update_hiwater_rss(mm); unmap_vmas(&tlb, vma, start, end, &nr_accounted, NULL); vm_unacct_memory(nr_accounted); - free_pgtables(&tlb, vma, prev? prev->vm_end: FIRST_USER_ADDRESS, + free_pgtables(tlb, vma, prev? prev->vm_end: FIRST_USER_ADDRESS, next? next->vm_start: 0); tlb_finish_mmu(tlb, start, end); } @@ -2063,7 +2065,7 @@ void exit_mmap(struct mm_struct *mm) /* Use -1 here to ensure all VMAs in the mm are unmapped */ end = unmap_vmas(&tlb, vma, 0, -1, &nr_accounted, NULL); vm_unacct_memory(nr_accounted); - free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, 0); + free_pgtables(tlb, vma, FIRST_USER_ADDRESS, 0); tlb_finish_mmu(tlb, 0, end); /* -- cgit From da3bbdd4632c0171406b2677e31494afa5bde2f8 Mon Sep 17 00:00:00 2001 From: Kentaro Makita Date: Wed, 23 Jul 2008 21:27:13 -0700 Subject: fix soft lock up at NFS mount via per-SB LRU-list of unused dentries [Summary] Split LRU-list of unused dentries to one per superblock to avoid soft lock up during NFS mounts and remounting of any filesystem. Previously I posted here: http://lkml.org/lkml/2008/3/5/590 [Descriptions] - background dentry_unused is a list of dentries which are not referenced. dentry_unused grows up when references on directories or files are released. This list can be very long if there is huge free memory. - the problem When shrink_dcache_sb() is called, it scans all dentry_unused linearly under spin_lock(), and if dentry->d_sb is differnt from given superblock, scan next dentry. This scan costs very much if there are many entries, and very ineffective if there are many superblocks. IOW, When we need to shrink unused dentries on one dentry, but scans unused dentries on all superblocks in the system. For example, we scan 500 dentries to unmount a filesystem, but scans 1,000,000 or more unused dentries on other superblocks. In our case , At mounting NFS*, shrink_dcache_sb() is called to shrink unused dentries on NFS, but scans 100,000,000 unused dentries on superblocks in the system such as local ext3 filesystems. I hear NFS mounting took 1 min on some system in use. * : NFS uses virtual filesystem in rpc layer, so NFS is affected by this problem. 100,000,000 is possible number on large systems. Per-superblock LRU of unused dentried can reduce the cost in reasonable manner. - How to fix I found this problem is solved by David Chinner's "Per-superblock unused dentry LRU lists V3"(1), so I rebase it and add some fix to reclaim with fairness, which is in Andrew Morton's comments(2). 1) http://lkml.org/lkml/2006/5/25/318 2) http://lkml.org/lkml/2006/5/25/320 Split LRU-list of unused dentries to each superblocks. Then, NFS mounting will check dentries under a superblock instead of all. But this spliting will break LRU of dentry-unused. So, I've attempted to make reclaim unused dentrins with fairness by calculate number of dentries to scan on this sb based on following way number of dentries to scan on this sb = count * (number of dentries on this sb / number of dentries in the machine) - ToDo - I have to measuring performance number and do stress tests. - When unmount occurs during prune_dcache(), scanning on same superblock, It is unable to reach next superblock because it is gone away. We restart scannig superblock from first one, it causes unfairness of reclaim unused dentries on first superblock. But I think this happens very rarely. - Test Results Result on 6GB boxes with excessive unused dentries. Without patch: $ cat /proc/sys/fs/dentry-state 10181835 10180203 45 0 0 0 # mount -t nfs 10.124.60.70:/work/kernel-src nfs real 0m1.830s user 0m0.001s sys 0m1.653s With this patch: $ cat /proc/sys/fs/dentry-state 10236610 10234751 45 0 0 0 # mount -t nfs 10.124.60.70:/work/kernel-src nfs real 0m0.106s user 0m0.002s sys 0m0.032s [akpm@linux-foundation.org: fix comments] Signed-off-by: Kentaro Makita Cc: Neil Brown Cc: Trond Myklebust Cc: David Chinner Cc: "J. Bruce Fields" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/dcache.c | 335 ++++++++++++++++++++++++++++------------------------- fs/super.c | 1 + include/linux/fs.h | 4 + 3 files changed, 185 insertions(+), 155 deletions(-) (limited to 'include/linux') diff --git a/fs/dcache.c b/fs/dcache.c index 6068c25b393c..3818d6ab76ca 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -61,7 +61,6 @@ static struct kmem_cache *dentry_cache __read_mostly; static unsigned int d_hash_mask __read_mostly; static unsigned int d_hash_shift __read_mostly; static struct hlist_head *dentry_hashtable __read_mostly; -static LIST_HEAD(dentry_unused); /* Statistics gathering. */ struct dentry_stat_t dentry_stat = { @@ -96,14 +95,6 @@ static void d_free(struct dentry *dentry) call_rcu(&dentry->d_u.d_rcu, d_callback); } -static void dentry_lru_remove(struct dentry *dentry) -{ - if (!list_empty(&dentry->d_lru)) { - list_del_init(&dentry->d_lru); - dentry_stat.nr_unused--; - } -} - /* * Release the dentry's inode, using the filesystem * d_iput() operation if defined. @@ -130,6 +121,41 @@ static void dentry_iput(struct dentry * dentry) } } +/* + * dentry_lru_(add|add_tail|del|del_init) must be called with dcache_lock held. + */ +static void dentry_lru_add(struct dentry *dentry) +{ + list_add(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); + dentry->d_sb->s_nr_dentry_unused++; + dentry_stat.nr_unused++; +} + +static void dentry_lru_add_tail(struct dentry *dentry) +{ + list_add_tail(&dentry->d_lru, &dentry->d_sb->s_dentry_lru); + dentry->d_sb->s_nr_dentry_unused++; + dentry_stat.nr_unused++; +} + +static void dentry_lru_del(struct dentry *dentry) +{ + if (!list_empty(&dentry->d_lru)) { + list_del(&dentry->d_lru); + dentry->d_sb->s_nr_dentry_unused--; + dentry_stat.nr_unused--; + } +} + +static void dentry_lru_del_init(struct dentry *dentry) +{ + if (likely(!list_empty(&dentry->d_lru))) { + list_del_init(&dentry->d_lru); + dentry->d_sb->s_nr_dentry_unused--; + dentry_stat.nr_unused--; + } +} + /** * d_kill - kill dentry and return parent * @dentry: dentry to kill @@ -212,8 +238,7 @@ repeat: goto kill_it; if (list_empty(&dentry->d_lru)) { dentry->d_flags |= DCACHE_REFERENCED; - list_add(&dentry->d_lru, &dentry_unused); - dentry_stat.nr_unused++; + dentry_lru_add(dentry); } spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); @@ -222,7 +247,8 @@ repeat: unhash_it: __d_drop(dentry); kill_it: - dentry_lru_remove(dentry); + /* if dentry was on the d_lru list delete it from there */ + dentry_lru_del(dentry); dentry = d_kill(dentry); if (dentry) goto repeat; @@ -290,7 +316,7 @@ int d_invalidate(struct dentry * dentry) static inline struct dentry * __dget_locked(struct dentry *dentry) { atomic_inc(&dentry->d_count); - dentry_lru_remove(dentry); + dentry_lru_del_init(dentry); return dentry; } @@ -406,133 +432,167 @@ static void prune_one_dentry(struct dentry * dentry) if (dentry->d_op && dentry->d_op->d_delete) dentry->d_op->d_delete(dentry); - dentry_lru_remove(dentry); + dentry_lru_del_init(dentry); __d_drop(dentry); dentry = d_kill(dentry); spin_lock(&dcache_lock); } } -/** - * prune_dcache - shrink the dcache - * @count: number of entries to try and free - * @sb: if given, ignore dentries for other superblocks - * which are being unmounted. - * - * Shrink the dcache. This is done when we need - * more memory, or simply when we need to unmount - * something (at which point we need to unuse - * all dentries). - * - * This function may fail to free any resources if - * all the dentries are in use. +/* + * Shrink the dentry LRU on a given superblock. + * @sb : superblock to shrink dentry LRU. + * @count: If count is NULL, we prune all dentries on superblock. + * @flags: If flags is non-zero, we need to do special processing based on + * which flags are set. This means we don't need to maintain multiple + * similar copies of this loop. */ - -static void prune_dcache(int count, struct super_block *sb) +static void __shrink_dcache_sb(struct super_block *sb, int *count, int flags) { - spin_lock(&dcache_lock); - for (; count ; count--) { - struct dentry *dentry; - struct list_head *tmp; - struct rw_semaphore *s_umount; - - cond_resched_lock(&dcache_lock); + LIST_HEAD(referenced); + LIST_HEAD(tmp); + struct dentry *dentry; + int cnt = 0; - tmp = dentry_unused.prev; - if (sb) { - /* Try to find a dentry for this sb, but don't try - * too hard, if they aren't near the tail they will - * be moved down again soon + BUG_ON(!sb); + BUG_ON((flags & DCACHE_REFERENCED) && count == NULL); + spin_lock(&dcache_lock); + if (count != NULL) + /* called from prune_dcache() and shrink_dcache_parent() */ + cnt = *count; +restart: + if (count == NULL) + list_splice_init(&sb->s_dentry_lru, &tmp); + else { + while (!list_empty(&sb->s_dentry_lru)) { + dentry = list_entry(sb->s_dentry_lru.prev, + struct dentry, d_lru); + BUG_ON(dentry->d_sb != sb); + + spin_lock(&dentry->d_lock); + /* + * If we are honouring the DCACHE_REFERENCED flag and + * the dentry has this flag set, don't free it. Clear + * the flag and put it back on the LRU. */ - int skip = count; - while (skip && tmp != &dentry_unused && - list_entry(tmp, struct dentry, d_lru)->d_sb != sb) { - skip--; - tmp = tmp->prev; + if ((flags & DCACHE_REFERENCED) + && (dentry->d_flags & DCACHE_REFERENCED)) { + dentry->d_flags &= ~DCACHE_REFERENCED; + list_move_tail(&dentry->d_lru, &referenced); + spin_unlock(&dentry->d_lock); + } else { + list_move_tail(&dentry->d_lru, &tmp); + spin_unlock(&dentry->d_lock); + cnt--; + if (!cnt) + break; } } - if (tmp == &dentry_unused) - break; - list_del_init(tmp); - prefetch(dentry_unused.prev); - dentry_stat.nr_unused--; - dentry = list_entry(tmp, struct dentry, d_lru); - - spin_lock(&dentry->d_lock); + } + while (!list_empty(&tmp)) { + dentry = list_entry(tmp.prev, struct dentry, d_lru); + dentry_lru_del_init(dentry); + spin_lock(&dentry->d_lock); /* * We found an inuse dentry which was not removed from - * dentry_unused because of laziness during lookup. Do not free - * it - just keep it off the dentry_unused list. + * the LRU because of laziness during lookup. Do not free + * it - just keep it off the LRU list. */ - if (atomic_read(&dentry->d_count)) { - spin_unlock(&dentry->d_lock); + if (atomic_read(&dentry->d_count)) { + spin_unlock(&dentry->d_lock); continue; } - /* If the dentry was recently referenced, don't free it. */ - if (dentry->d_flags & DCACHE_REFERENCED) { - dentry->d_flags &= ~DCACHE_REFERENCED; - list_add(&dentry->d_lru, &dentry_unused); - dentry_stat.nr_unused++; - spin_unlock(&dentry->d_lock); + prune_one_dentry(dentry); + /* dentry->d_lock was dropped in prune_one_dentry() */ + cond_resched_lock(&dcache_lock); + } + if (count == NULL && !list_empty(&sb->s_dentry_lru)) + goto restart; + if (count != NULL) + *count = cnt; + if (!list_empty(&referenced)) + list_splice(&referenced, &sb->s_dentry_lru); + spin_unlock(&dcache_lock); +} + +/** + * prune_dcache - shrink the dcache + * @count: number of entries to try to free + * + * Shrink the dcache. This is done when we need more memory, or simply when we + * need to unmount something (at which point we need to unuse all dentries). + * + * This function may fail to free any resources if all the dentries are in use. + */ +static void prune_dcache(int count) +{ + struct super_block *sb; + int w_count; + int unused = dentry_stat.nr_unused; + int prune_ratio; + int pruned; + + if (unused == 0 || count == 0) + return; + spin_lock(&dcache_lock); +restart: + if (count >= unused) + prune_ratio = 1; + else + prune_ratio = unused / count; + spin_lock(&sb_lock); + list_for_each_entry(sb, &super_blocks, s_list) { + if (sb->s_nr_dentry_unused == 0) continue; - } - /* - * If the dentry is not DCACHED_REFERENCED, it is time - * to remove it from the dcache, provided the super block is - * NULL (which means we are trying to reclaim memory) - * or this dentry belongs to the same super block that - * we want to shrink. - */ - /* - * If this dentry is for "my" filesystem, then I can prune it - * without taking the s_umount lock (I already hold it). + sb->s_count++; + /* Now, we reclaim unused dentrins with fairness. + * We reclaim them same percentage from each superblock. + * We calculate number of dentries to scan on this sb + * as follows, but the implementation is arranged to avoid + * overflows: + * number of dentries to scan on this sb = + * count * (number of dentries on this sb / + * number of dentries in the machine) */ - if (sb && dentry->d_sb == sb) { - prune_one_dentry(dentry); - continue; - } + spin_unlock(&sb_lock); + if (prune_ratio != 1) + w_count = (sb->s_nr_dentry_unused / prune_ratio) + 1; + else + w_count = sb->s_nr_dentry_unused; + pruned = w_count; /* - * ...otherwise we need to be sure this filesystem isn't being - * unmounted, otherwise we could race with - * generic_shutdown_super(), and end up holding a reference to - * an inode while the filesystem is unmounted. - * So we try to get s_umount, and make sure s_root isn't NULL. - * (Take a local copy of s_umount to avoid a use-after-free of - * `dentry'). + * We need to be sure this filesystem isn't being unmounted, + * otherwise we could race with generic_shutdown_super(), and + * end up holding a reference to an inode while the filesystem + * is unmounted. So we try to get s_umount, and make sure + * s_root isn't NULL. */ - s_umount = &dentry->d_sb->s_umount; - if (down_read_trylock(s_umount)) { - if (dentry->d_sb->s_root != NULL) { - prune_one_dentry(dentry); - up_read(s_umount); - continue; + if (down_read_trylock(&sb->s_umount)) { + if ((sb->s_root != NULL) && + (!list_empty(&sb->s_dentry_lru))) { + spin_unlock(&dcache_lock); + __shrink_dcache_sb(sb, &w_count, + DCACHE_REFERENCED); + pruned -= w_count; + spin_lock(&dcache_lock); } - up_read(s_umount); + up_read(&sb->s_umount); } - spin_unlock(&dentry->d_lock); + spin_lock(&sb_lock); + count -= pruned; /* - * Insert dentry at the head of the list as inserting at the - * tail leads to a cycle. + * restart only when sb is no longer on the list and + * we have more work to do. */ - list_add(&dentry->d_lru, &dentry_unused); - dentry_stat.nr_unused++; + if (__put_super_and_need_restart(sb) && count > 0) { + spin_unlock(&sb_lock); + goto restart; + } } + spin_unlock(&sb_lock); spin_unlock(&dcache_lock); } -/* - * Shrink the dcache for the specified super block. - * This allows us to unmount a device without disturbing - * the dcache for the other devices. - * - * This implementation makes just two traversals of the - * unused list. On the first pass we move the selected - * dentries to the most recent end, and on the second - * pass we free them. The second pass must restart after - * each dput(), but since the target dentries are all at - * the end, it's really just a single traversal. - */ - /** * shrink_dcache_sb - shrink dcache for a superblock * @sb: superblock @@ -541,44 +601,9 @@ static void prune_dcache(int count, struct super_block *sb) * is used to free the dcache before unmounting a file * system */ - void shrink_dcache_sb(struct super_block * sb) { - struct list_head *tmp, *next; - struct dentry *dentry; - - /* - * Pass one ... move the dentries for the specified - * superblock to the most recent end of the unused list. - */ - spin_lock(&dcache_lock); - list_for_each_prev_safe(tmp, next, &dentry_unused) { - dentry = list_entry(tmp, struct dentry, d_lru); - if (dentry->d_sb != sb) - continue; - list_move_tail(tmp, &dentry_unused); - } - - /* - * Pass two ... free the dentries for this superblock. - */ -repeat: - list_for_each_prev_safe(tmp, next, &dentry_unused) { - dentry = list_entry(tmp, struct dentry, d_lru); - if (dentry->d_sb != sb) - continue; - dentry_stat.nr_unused--; - list_del_init(tmp); - spin_lock(&dentry->d_lock); - if (atomic_read(&dentry->d_count)) { - spin_unlock(&dentry->d_lock); - continue; - } - prune_one_dentry(dentry); - cond_resched_lock(&dcache_lock); - goto repeat; - } - spin_unlock(&dcache_lock); + __shrink_dcache_sb(sb, NULL, 0); } /* @@ -595,7 +620,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) /* detach this root from the system */ spin_lock(&dcache_lock); - dentry_lru_remove(dentry); + dentry_lru_del_init(dentry); __d_drop(dentry); spin_unlock(&dcache_lock); @@ -609,7 +634,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) spin_lock(&dcache_lock); list_for_each_entry(loop, &dentry->d_subdirs, d_u.d_child) { - dentry_lru_remove(loop); + dentry_lru_del_init(loop); __d_drop(loop); cond_resched_lock(&dcache_lock); } @@ -791,14 +816,13 @@ resume: struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child); next = tmp->next; - dentry_lru_remove(dentry); + dentry_lru_del_init(dentry); /* * move only zero ref count dentries to the end * of the unused list for prune_dcache */ if (!atomic_read(&dentry->d_count)) { - list_add_tail(&dentry->d_lru, &dentry_unused); - dentry_stat.nr_unused++; + dentry_lru_add_tail(dentry); found++; } @@ -840,10 +864,11 @@ out: void shrink_dcache_parent(struct dentry * parent) { + struct super_block *sb = parent->d_sb; int found; while ((found = select_parent(parent)) != 0) - prune_dcache(found, parent->d_sb); + __shrink_dcache_sb(sb, &found, 0); } /* @@ -863,7 +888,7 @@ static int shrink_dcache_memory(int nr, gfp_t gfp_mask) if (nr) { if (!(gfp_mask & __GFP_FS)) return -1; - prune_dcache(nr, NULL); + prune_dcache(nr); } return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure; } @@ -1215,7 +1240,7 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) * rcu_read_lock() and rcu_read_unlock() are used to disable preemption while * lookup is going on. * - * dentry_unused list is not updated even if lookup finds the required dentry + * The dentry unused LRU is not updated even if lookup finds the required dentry * in there. It is updated in places such as prune_dcache, shrink_dcache_sb, * select_parent and __dget_locked. This laziness saves lookup from dcache_lock * acquisition. diff --git a/fs/super.c b/fs/super.c index 453877c5697b..e931ae9511fe 100644 --- a/fs/super.c +++ b/fs/super.c @@ -70,6 +70,7 @@ static struct super_block *alloc_super(struct file_system_type *type) INIT_LIST_HEAD(&s->s_instances); INIT_HLIST_HEAD(&s->s_anon); INIT_LIST_HEAD(&s->s_inodes); + INIT_LIST_HEAD(&s->s_dentry_lru); init_rwsem(&s->s_umount); mutex_init(&s->s_lock); lockdep_set_class(&s->s_umount, &type->s_umount_key); diff --git a/include/linux/fs.h b/include/linux/fs.h index ff54ae4933f3..e5e6a244096c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1025,6 +1025,7 @@ extern int send_sigurg(struct fown_struct *fown); extern struct list_head super_blocks; extern spinlock_t sb_lock; +#define sb_entry(list) list_entry((list), struct super_block, s_list) #define S_BIAS (1<<30) struct super_block { struct list_head s_list; /* Keep this first */ @@ -1058,6 +1059,9 @@ struct super_block { struct list_head s_more_io; /* parked for more writeback */ struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ struct list_head s_files; + /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */ + struct list_head s_dentry_lru; /* unused dentry lru */ + int s_nr_dentry_unused; /* # of dentry on lru */ struct block_device *s_bdev; struct mtd_info *s_mtd; -- cgit From 0cad47cf13bc2e9142d3a11d9f50523797d0d4ea Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Wed, 23 Jul 2008 21:27:16 -0700 Subject: page-flags: record page flag overlays explicitly With the recent page flag reorganisation we have a single enum which defines the valid page flags and their values, nice and clear. However there are a number of bits which are overloaded by different subsystems. Firstly there is PG_owner_priv_1 which is used by filesystems and by XEN. Secondly both SLOB and SLUB use a couple of extra page bits to manage internal state for pages they own; both overlay other bits. All of these "aliases" are scattered about the source making it very hard for a reader to know if the bits are safe to rely on in all contexts; confusion here is bad. As we now have a single place where the bits are clearly assigned it makes sense to clarify the reuse of bits by making the aliases explicit and visible with the original bit assignments. This patch creates explicit aliases within the enum itself for the overloaded bits, creates standard bit accessors PageFoo etc. and uses those throughout. This version pulls the bit manipulation out to standard named page bit accessors as suggested by Christoph, it retains the explicit mapping to the overlayed bits. A fusion of both ideas. This has been SLUB and SLOB have been compile tested on x86_64 only, and SLUB boot tested. If people feel this is worth doing then I can run a fuller set of testing. This patch: Some page flags are used for more than one purpose, for example PG_owner_priv_1. Currently there are individual accessors for each user, each built using the common flag name far away from the bit definitions. This makes it hard to see all possible uses of these bits. Now that we have a single enum to generate the bit orders it makes sense to express overlays in the same place. So create per use aliases for this bit in the main page-flags enum and use those in the accessors. [akpm@linux-foundation.org: fix xen] Signed-off-by: Andy Whitcroft Cc: Pekka Enberg Cc: Christoph Lameter Cc: Matt Mackall Cc: Nick Piggin Cc: KAMEZAWA Hiroyuki Reviewed-by: KOSAKI Motohiro Cc: Rik van Riel Cc: Jeremy Fitzhardinge Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/page-flags.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 0d2a4e7012aa..7d8db1233e44 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -96,7 +96,14 @@ enum pageflags { #ifdef CONFIG_IA64_UNCACHED_ALLOCATOR PG_uncached, /* Page has been mapped as uncached */ #endif - __NR_PAGEFLAGS + __NR_PAGEFLAGS, + + /* Filesystems */ + PG_checked = PG_owner_priv_1, + + /* XEN */ + PG_pinned = PG_owner_priv_1, + PG_savepinned = PG_dirty, }; #ifndef __GENERATING_BOUNDS_H @@ -155,9 +162,9 @@ PAGEFLAG(Dirty, dirty) TESTSCFLAG(Dirty, dirty) __CLEARPAGEFLAG(Dirty, dirty) PAGEFLAG(LRU, lru) __CLEARPAGEFLAG(LRU, lru) PAGEFLAG(Active, active) __CLEARPAGEFLAG(Active, active) __PAGEFLAG(Slab, slab) -PAGEFLAG(Checked, owner_priv_1) /* Used by some filesystems */ -PAGEFLAG(Pinned, owner_priv_1) TESTSCFLAG(Pinned, owner_priv_1) /* Xen */ -PAGEFLAG(SavePinned, dirty); /* Xen */ +PAGEFLAG(Checked, checked) /* Used by some filesystems */ +PAGEFLAG(Pinned, pinned) TESTSCFLAG(Pinned, pinned) /* Xen */ +PAGEFLAG(SavePinned, savepinned); /* Xen */ PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved) PAGEFLAG(Private, private) __CLEARPAGEFLAG(Private, private) __SETPAGEFLAG(Private, private) -- cgit From 8a38082d21cbc5ec961da7dda195e98a9a064dcf Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Wed, 23 Jul 2008 21:27:18 -0700 Subject: slub: record page flag overlays explicitly SLUB reuses two page bits for internal purposes, it overlays PG_active and PG_error. This is hidden away in slub.c. Document these overlays explicitly in the main page-flags enum along with all the others. Signed-off-by: Andy Whitcroft Cc: Pekka Enberg Cc: Christoph Lameter Cc: Matt Mackall Cc: Nick Piggin Tested-by: KOSAKI Motohiro Cc: KOSAKI Motohiro Cc: Rik van Riel Cc: Jeremy Fitzhardinge Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/page-flags.h | 7 +++++ mm/slub.c | 65 ++++++++++++---------------------------------- 2 files changed, 24 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 7d8db1233e44..3fc586b7b90b 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -104,6 +104,10 @@ enum pageflags { /* XEN */ PG_pinned = PG_owner_priv_1, PG_savepinned = PG_dirty, + + /* SLUB */ + PG_slub_frozen = PG_active, + PG_slub_debug = PG_error, }; #ifndef __GENERATING_BOUNDS_H @@ -169,6 +173,9 @@ PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved) PAGEFLAG(Private, private) __CLEARPAGEFLAG(Private, private) __SETPAGEFLAG(Private, private) +__PAGEFLAG(SlubFrozen, slub_frozen) +__PAGEFLAG(SlubDebug, slub_debug) + /* * Only test-and-set exist for PG_writeback. The unconditional operators are * risky: they bypass page accounting. diff --git a/mm/slub.c b/mm/slub.c index 6d4a49c1ff2f..77c21cf53ff9 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -102,44 +102,12 @@ * the fast path and disables lockless freelists. */ -#define FROZEN (1 << PG_active) - #ifdef CONFIG_SLUB_DEBUG -#define SLABDEBUG (1 << PG_error) +#define SLABDEBUG 1 #else #define SLABDEBUG 0 #endif -static inline int SlabFrozen(struct page *page) -{ - return page->flags & FROZEN; -} - -static inline void SetSlabFrozen(struct page *page) -{ - page->flags |= FROZEN; -} - -static inline void ClearSlabFrozen(struct page *page) -{ - page->flags &= ~FROZEN; -} - -static inline int SlabDebug(struct page *page) -{ - return page->flags & SLABDEBUG; -} - -static inline void SetSlabDebug(struct page *page) -{ - page->flags |= SLABDEBUG; -} - -static inline void ClearSlabDebug(struct page *page) -{ - page->flags &= ~SLABDEBUG; -} - /* * Issues still to be resolved: * @@ -971,7 +939,7 @@ static int free_debug_processing(struct kmem_cache *s, struct page *page, } /* Special debug activities for freeing objects */ - if (!SlabFrozen(page) && !page->freelist) + if (!PageSlubFrozen(page) && !page->freelist) remove_full(s, page); if (s->flags & SLAB_STORE_USER) set_track(s, object, TRACK_FREE, addr); @@ -1157,7 +1125,7 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) page->flags |= 1 << PG_slab; if (s->flags & (SLAB_DEBUG_FREE | SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | SLAB_TRACE)) - SetSlabDebug(page); + __SetPageSlubDebug(page); start = page_address(page); @@ -1184,14 +1152,14 @@ static void __free_slab(struct kmem_cache *s, struct page *page) int order = compound_order(page); int pages = 1 << order; - if (unlikely(SlabDebug(page))) { + if (unlikely(SLABDEBUG && PageSlubDebug(page))) { void *p; slab_pad_check(s, page); for_each_object(p, s, page_address(page), page->objects) check_object(s, page, p, 0); - ClearSlabDebug(page); + __ClearPageSlubDebug(page); } mod_zone_page_state(page_zone(page), @@ -1288,7 +1256,7 @@ static inline int lock_and_freeze_slab(struct kmem_cache_node *n, if (slab_trylock(page)) { list_del(&page->lru); n->nr_partial--; - SetSlabFrozen(page); + __SetPageSlubFrozen(page); return 1; } return 0; @@ -1398,7 +1366,7 @@ static void unfreeze_slab(struct kmem_cache *s, struct page *page, int tail) struct kmem_cache_node *n = get_node(s, page_to_nid(page)); struct kmem_cache_cpu *c = get_cpu_slab(s, smp_processor_id()); - ClearSlabFrozen(page); + __ClearPageSlubFrozen(page); if (page->inuse) { if (page->freelist) { @@ -1406,7 +1374,8 @@ static void unfreeze_slab(struct kmem_cache *s, struct page *page, int tail) stat(c, tail ? DEACTIVATE_TO_TAIL : DEACTIVATE_TO_HEAD); } else { stat(c, DEACTIVATE_FULL); - if (SlabDebug(page) && (s->flags & SLAB_STORE_USER)) + if (SLABDEBUG && PageSlubDebug(page) && + (s->flags & SLAB_STORE_USER)) add_full(n, page); } slab_unlock(page); @@ -1551,7 +1520,7 @@ load_freelist: object = c->page->freelist; if (unlikely(!object)) goto another_slab; - if (unlikely(SlabDebug(c->page))) + if (unlikely(SLABDEBUG && PageSlubDebug(c->page))) goto debug; c->freelist = object[c->offset]; @@ -1588,7 +1557,7 @@ new_slab: if (c->page) flush_slab(s, c); slab_lock(new); - SetSlabFrozen(new); + __SetPageSlubFrozen(new); c->page = new; goto load_freelist; } @@ -1674,7 +1643,7 @@ static void __slab_free(struct kmem_cache *s, struct page *page, stat(c, FREE_SLOWPATH); slab_lock(page); - if (unlikely(SlabDebug(page))) + if (unlikely(SLABDEBUG && PageSlubDebug(page))) goto debug; checks_ok: @@ -1682,7 +1651,7 @@ checks_ok: page->freelist = object; page->inuse--; - if (unlikely(SlabFrozen(page))) { + if (unlikely(PageSlubFrozen(page))) { stat(c, FREE_FROZEN); goto out_unlock; } @@ -3317,12 +3286,12 @@ static void validate_slab_slab(struct kmem_cache *s, struct page *page, s->name, page); if (s->flags & DEBUG_DEFAULT_FLAGS) { - if (!SlabDebug(page)) - printk(KERN_ERR "SLUB %s: SlabDebug not set " + if (!PageSlubDebug(page)) + printk(KERN_ERR "SLUB %s: SlubDebug not set " "on slab 0x%p\n", s->name, page); } else { - if (SlabDebug(page)) - printk(KERN_ERR "SLUB %s: SlabDebug set on " + if (PageSlubDebug(page)) + printk(KERN_ERR "SLUB %s: SlubDebug set on " "slab 0x%p\n", s->name, page); } } -- cgit From 9023cb7e8564d95a1893f8cb6895a293be9a71fe Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Wed, 23 Jul 2008 21:27:19 -0700 Subject: slob: record page flag overlays explicitly SLOB reuses two page bits for internal purposes, it overlays PG_active and PG_private. This is hidden away in slob.c. Document these overlays explicitly in the main page-flags enum along with all the others. Signed-off-by: Andy Whitcroft Cc: Pekka Enberg Cc: Christoph Lameter Cc: Matt Mackall Cc: Nick Piggin Reviewed-by: KOSAKI Motohiro Cc: KOSAKI Motohiro Cc: Rik van Riel Cc: Jeremy Fitzhardinge Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/page-flags.h | 7 +++++++ mm/slob.c | 12 ++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 3fc586b7b90b..54590a9a103e 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -105,6 +105,10 @@ enum pageflags { PG_pinned = PG_owner_priv_1, PG_savepinned = PG_dirty, + /* SLOB */ + PG_slob_page = PG_active, + PG_slob_free = PG_private, + /* SLUB */ PG_slub_frozen = PG_active, PG_slub_debug = PG_error, @@ -173,6 +177,9 @@ PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved) PAGEFLAG(Private, private) __CLEARPAGEFLAG(Private, private) __SETPAGEFLAG(Private, private) +__PAGEFLAG(SlobPage, slob_page) +__PAGEFLAG(SlobFree, slob_free) + __PAGEFLAG(SlubFrozen, slub_frozen) __PAGEFLAG(SlubDebug, slub_debug) diff --git a/mm/slob.c b/mm/slob.c index a3ad6671adf1..de268eb7ac70 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -130,17 +130,17 @@ static LIST_HEAD(free_slob_large); */ static inline int slob_page(struct slob_page *sp) { - return test_bit(PG_active, &sp->flags); + return PageSlobPage((struct page *)sp); } static inline void set_slob_page(struct slob_page *sp) { - __set_bit(PG_active, &sp->flags); + __SetPageSlobPage((struct page *)sp); } static inline void clear_slob_page(struct slob_page *sp) { - __clear_bit(PG_active, &sp->flags); + __ClearPageSlobPage((struct page *)sp); } /* @@ -148,19 +148,19 @@ static inline void clear_slob_page(struct slob_page *sp) */ static inline int slob_page_free(struct slob_page *sp) { - return test_bit(PG_private, &sp->flags); + return PageSlobFree((struct page *)sp); } static void set_slob_page_free(struct slob_page *sp, struct list_head *list) { list_add(&sp->list, list); - __set_bit(PG_private, &sp->flags); + __SetPageSlobFree((struct page *)sp); } static inline void clear_slob_page_free(struct slob_page *sp) { list_del(&sp->list); - __clear_bit(PG_private, &sp->flags); + __ClearPageSlobFree((struct page *)sp); } #define SLOB_UNIT sizeof(slob_t) -- cgit From 2185e69f680ae8c8496b6fc15e20c889d5b39b67 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 23 Jul 2008 21:27:19 -0700 Subject: mapping_set_error: add unlikely() This is called on a per-page basis and in the vast majority of cases `error' is zero. Cc: Guillaume Chazarain Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- 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 d2fca802f809..ee1ec2c7723c 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -22,7 +22,7 @@ static inline void mapping_set_error(struct address_space *mapping, int error) { - if (error) { + if (unlikely(error)) { if (error == -ENOSPC) set_bit(AS_ENOSPC, &mapping->flags); else -- cgit From 9109fb7b3520de187ebc3646c209d66a233f7169 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:27:20 -0700 Subject: mm: drop unneeded pgdat argument from free_area_init_node() free_area_init_node() gets passed in the node id as well as the node descriptor. This is redundant as the function can trivially get the node descriptor itself by means of NODE_DATA() and the node's id. I checked all the users and NODE_DATA() seems to be usable everywhere from where this function is called. Signed-off-by: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/mm/numa.c | 2 +- arch/arm/mm/init.c | 2 +- arch/avr32/mm/init.c | 2 +- arch/cris/arch-v10/mm/init.c | 2 +- arch/cris/arch-v32/mm/init.c | 2 +- arch/m32r/mm/discontig.c | 3 +-- arch/m32r/mm/init.c | 2 +- arch/m68k/mm/motorola.c | 2 +- arch/m68k/mm/sun3mmu.c | 2 +- arch/parisc/mm/init.c | 2 +- arch/sparc/mm/srmmu.c | 3 +-- arch/sparc/mm/sun4c.c | 3 +-- arch/v850/kernel/setup.c | 3 +-- include/linux/mm.h | 5 ++--- mm/memory_hotplug.c | 2 +- mm/page_alloc.c | 11 ++++++----- 16 files changed, 22 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index a53fda0481ca..def0c74a78a8 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -313,7 +313,7 @@ void __init paging_init(void) zones_size[ZONE_DMA] = dma_local_pfn; zones_size[ZONE_NORMAL] = (end_pfn - start_pfn) - dma_local_pfn; } - free_area_init_node(nid, NODE_DATA(nid), zones_size, start_pfn, NULL); + free_area_init_node(nid, zones_size, start_pfn, NULL); } /* Initialize the kernel's ZERO_PGE. */ diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index b657f1719af0..e6352946dde0 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -284,7 +284,7 @@ bootmem_init_node(int node, int initrd_node, struct meminfo *mi) */ arch_adjust_zones(node, zone_size, zhole_size); - free_area_init_node(node, pgdat, zone_size, start_pfn, zhole_size); + free_area_init_node(node, zone_size, start_pfn, zhole_size); return end_pfn; } diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 3f90a87527bb..786de88a82a7 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -129,7 +129,7 @@ void __init paging_init(void) printk("Node %u: start_pfn = 0x%lx, low = 0x%lx\n", nid, start_pfn, low); - free_area_init_node(nid, pgdat, zones_size, start_pfn, NULL); + free_area_init_node(nid, zones_size, start_pfn, NULL); printk("Node %u: mem_map starts at %p\n", pgdat->node_id, pgdat->node_mem_map); diff --git a/arch/cris/arch-v10/mm/init.c b/arch/cris/arch-v10/mm/init.c index e0fcd1a9bfd5..742fd1974c2e 100644 --- a/arch/cris/arch-v10/mm/init.c +++ b/arch/cris/arch-v10/mm/init.c @@ -182,7 +182,7 @@ paging_init(void) * mem_map page array. */ - free_area_init_node(0, &contig_page_data, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); + free_area_init_node(0, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); } /* Initialize remaps of some I/O-ports. It is important that this diff --git a/arch/cris/arch-v32/mm/init.c b/arch/cris/arch-v32/mm/init.c index 5a9ac5834647..8a34b8b74293 100644 --- a/arch/cris/arch-v32/mm/init.c +++ b/arch/cris/arch-v32/mm/init.c @@ -162,7 +162,7 @@ paging_init(void) * substantially higher than 0, like us (we start at PAGE_OFFSET). This * saves space in the mem_map page array. */ - free_area_init_node(0, &contig_page_data, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); + free_area_init_node(0, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); mem_map = contig_page_data.node_mem_map; } diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c index aa9145ef6cca..cc23934bc41e 100644 --- a/arch/m32r/mm/discontig.c +++ b/arch/m32r/mm/discontig.c @@ -147,8 +147,7 @@ unsigned long __init zone_sizes_init(void) zholes_size[ZONE_DMA] = mp->holes; holes += zholes_size[ZONE_DMA]; - free_area_init_node(nid, NODE_DATA(nid), zones_size, - start_pfn, zholes_size); + free_area_init_node(nid, zones_size, start_pfn, zholes_size); } /* diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index bbd97c85bc5d..28799af15e95 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c @@ -123,7 +123,7 @@ unsigned long __init zone_sizes_init(void) start_pfn = __MEMORY_START >> PAGE_SHIFT; #endif /* CONFIG_MMU */ - free_area_init_node(0, NODE_DATA(0), zones_size, start_pfn, 0); + free_area_init_node(0, zones_size, start_pfn, 0); return 0; } diff --git a/arch/m68k/mm/motorola.c b/arch/m68k/mm/motorola.c index 226795bdf355..c5dbb9bdb322 100644 --- a/arch/m68k/mm/motorola.c +++ b/arch/m68k/mm/motorola.c @@ -296,7 +296,7 @@ void __init paging_init(void) #endif for (i = 0; i < m68k_num_memory; i++) { zones_size[ZONE_DMA] = m68k_memory[i].size >> PAGE_SHIFT; - free_area_init_node(i, pg_data_map + i, zones_size, + free_area_init_node(i, zones_size, m68k_memory[i].addr >> PAGE_SHIFT, NULL); } } diff --git a/arch/m68k/mm/sun3mmu.c b/arch/m68k/mm/sun3mmu.c index edceefc18870..1b902dbd4376 100644 --- a/arch/m68k/mm/sun3mmu.c +++ b/arch/m68k/mm/sun3mmu.c @@ -94,7 +94,7 @@ void __init paging_init(void) /* I really wish I knew why the following change made things better... -- Sam */ /* free_area_init(zones_size); */ - free_area_init_node(0, NODE_DATA(0), zones_size, + free_area_init_node(0, zones_size, (__pa(PAGE_OFFSET) >> PAGE_SHIFT) + 1, NULL); diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index 0ddf4904640a..7c155c254e72 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -887,7 +887,7 @@ void __init paging_init(void) } #endif - free_area_init_node(i, NODE_DATA(i), zones_size, + free_area_init_node(i, zones_size, pmem_ranges[i].start_pfn, NULL); } } diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index c624e04ff03e..ee30462598fc 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1352,8 +1352,7 @@ void __init srmmu_paging_init(void) zones_size[ZONE_HIGHMEM] = npages; zholes_size[ZONE_HIGHMEM] = npages - calc_highpages(); - free_area_init_node(0, &contig_page_data, zones_size, - pfn_base, zholes_size); + free_area_init_node(0, zones_size, pfn_base, zholes_size); } } diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index 2375fe9dc312..d1782f6368be 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -2123,8 +2123,7 @@ void __init sun4c_paging_init(void) zones_size[ZONE_HIGHMEM] = npages; zholes_size[ZONE_HIGHMEM] = npages - calc_highpages(); - free_area_init_node(0, &contig_page_data, zones_size, - pfn_base, zholes_size); + free_area_init_node(0, zones_size, pfn_base, zholes_size); } cnt = 0; diff --git a/arch/v850/kernel/setup.c b/arch/v850/kernel/setup.c index a0a8456a8430..10335cecf7bd 100644 --- a/arch/v850/kernel/setup.c +++ b/arch/v850/kernel/setup.c @@ -295,8 +295,7 @@ init_mem_alloc (unsigned long ram_start, unsigned long ram_len) #error MAX_ORDER is too large for given PAGE_OFFSET (use CONFIG_FORCE_MAX_ZONEORDER to change it) #endif NODE_DATA(0)->node_mem_map = NULL; - free_area_init_node (0, NODE_DATA(0), zones_size, - ADDR_TO_PAGE (PAGE_OFFSET), 0); + free_area_init_node(0, zones_size, ADDR_TO_PAGE (PAGE_OFFSET), 0); } diff --git a/include/linux/mm.h b/include/linux/mm.h index f8071097302a..196924b657bc 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -962,9 +962,8 @@ static inline void pgtable_page_dtor(struct page *page) NULL: pte_offset_kernel(pmd, address)) extern void free_area_init(unsigned long * zones_size); -extern void free_area_init_node(int nid, pg_data_t *pgdat, - unsigned long * zones_size, unsigned long zone_start_pfn, - unsigned long *zholes_size); +extern void free_area_init_node(int nid, unsigned long * zones_size, + unsigned long zone_start_pfn, unsigned long *zholes_size); #ifdef CONFIG_ARCH_POPULATES_NODE_MAP /* * With CONFIG_ARCH_POPULATES_NODE_MAP set, an architecture may initialise its diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 833f854eabe5..6e26adc08f14 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -455,7 +455,7 @@ static pg_data_t *hotadd_new_pgdat(int nid, u64 start) /* we can use NODE_DATA(nid) from here */ /* init node's zones as empty zones, we don't have any present pages.*/ - free_area_init_node(nid, pgdat, zones_size, start_pfn, zholes_size); + free_area_init_node(nid, zones_size, start_pfn, zholes_size); return pgdat; } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 24aa3d1b9d96..e43aae135b38 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3461,10 +3461,11 @@ static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat) #endif /* CONFIG_FLAT_NODE_MEM_MAP */ } -void __paginginit free_area_init_node(int nid, struct pglist_data *pgdat, - unsigned long *zones_size, unsigned long node_start_pfn, - unsigned long *zholes_size) +void __paginginit free_area_init_node(int nid, unsigned long *zones_size, + unsigned long node_start_pfn, unsigned long *zholes_size) { + pg_data_t *pgdat = NODE_DATA(nid); + pgdat->node_id = nid; pgdat->node_start_pfn = node_start_pfn; calculate_node_totalpages(pgdat, zones_size, zholes_size); @@ -3961,7 +3962,7 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn) setup_nr_node_ids(); for_each_online_node(nid) { pg_data_t *pgdat = NODE_DATA(nid); - free_area_init_node(nid, pgdat, NULL, + free_area_init_node(nid, NULL, find_min_pfn_for_node(nid), NULL); /* Any memory on that node */ @@ -4032,7 +4033,7 @@ EXPORT_SYMBOL(contig_page_data); void __init free_area_init(unsigned long *zones_size) { - free_area_init_node(0, NODE_DATA(0), zones_size, + free_area_init_node(0, zones_size, __pa(PAGE_OFFSET) >> PAGE_SHIFT, NULL); } -- cgit From a1e78772d72b2616ed20e54896e68e0e7044854e Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 23 Jul 2008 21:27:23 -0700 Subject: hugetlb: reserve huge pages for reliable MAP_PRIVATE hugetlbfs mappings until fork() This patch reserves huge pages at mmap() time for MAP_PRIVATE mappings in a similar manner to the reservations taken for MAP_SHARED mappings. The reserve count is accounted both globally and on a per-VMA basis for private mappings. This guarantees that a process that successfully calls mmap() will successfully fault all pages in the future unless fork() is called. The characteristics of private mappings of hugetlbfs files behaviour after this patch are; 1. The process calling mmap() is guaranteed to succeed all future faults until it forks(). 2. On fork(), the parent may die due to SIGKILL on writes to the private mapping if enough pages are not available for the COW. For reasonably reliable behaviour in the face of a small huge page pool, children of hugepage-aware processes should not reference the mappings; such as might occur when fork()ing to exec(). 3. On fork(), the child VMAs inherit no reserves. Reads on pages already faulted by the parent will succeed. Successful writes will depend on enough huge pages being free in the pool. 4. Quotas of the hugetlbfs mount are checked at reserve time for the mapper and at fault time otherwise. Before this patch, all reads or writes in the child potentially needs page allocations that can later lead to the death of the parent. This applies to reads and writes of uninstantiated pages as well as COW. After the patch it is only a write to an instantiated page that causes problems. Signed-off-by: Mel Gorman Acked-by: Adam Litke Cc: Andy Whitcroft Cc: William Lee Irwin III Cc: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/hugetlbfs/inode.c | 8 +-- include/linux/hugetlb.h | 9 ++- kernel/fork.c | 9 +++ mm/hugetlb.c | 158 ++++++++++++++++++++++++++++++++++++------------ 4 files changed, 140 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index aeabf80f81a5..1576bbecd084 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -103,9 +103,9 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) ret = -ENOMEM; len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); - if (vma->vm_flags & VM_MAYSHARE && - hugetlb_reserve_pages(inode, vma->vm_pgoff >> (HPAGE_SHIFT-PAGE_SHIFT), - len >> HPAGE_SHIFT)) + if (hugetlb_reserve_pages(inode, + vma->vm_pgoff >> (HPAGE_SHIFT-PAGE_SHIFT), + len >> HPAGE_SHIFT, vma)) goto out; ret = 0; @@ -942,7 +942,7 @@ struct file *hugetlb_file_setup(const char *name, size_t size) goto out_dentry; error = -ENOMEM; - if (hugetlb_reserve_pages(inode, 0, size >> HPAGE_SHIFT)) + if (hugetlb_reserve_pages(inode, 0, size >> HPAGE_SHIFT, NULL)) goto out_inode; d_instantiate(dentry, inode); diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index a79e80b689d8..185b14c9f021 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -17,6 +17,7 @@ static inline int is_vm_hugetlb_page(struct vm_area_struct *vma) return vma->vm_flags & VM_HUGETLB; } +void reset_vma_resv_huge_pages(struct vm_area_struct *vma); int hugetlb_sysctl_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); int hugetlb_overcommit_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); int hugetlb_treat_movable_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); @@ -30,7 +31,8 @@ int hugetlb_report_node_meminfo(int, char *); unsigned long hugetlb_total_pages(void); int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, int write_access); -int hugetlb_reserve_pages(struct inode *inode, long from, long to); +int hugetlb_reserve_pages(struct inode *inode, long from, long to, + struct vm_area_struct *vma); void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed); extern unsigned long max_huge_pages; @@ -58,6 +60,11 @@ static inline int is_vm_hugetlb_page(struct vm_area_struct *vma) { return 0; } + +static inline void reset_vma_resv_huge_pages(struct vm_area_struct *vma) +{ +} + static inline unsigned long hugetlb_total_pages(void) { return 0; diff --git a/kernel/fork.c b/kernel/fork.c index adefc1131f27..552c8d8e77ad 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -306,6 +307,14 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) spin_unlock(&file->f_mapping->i_mmap_lock); } + /* + * Clear hugetlb-related page reserves for children. This only + * affects MAP_PRIVATE mappings. Faults generated by the child + * are not guaranteed to succeed, even if read-only + */ + if (is_vm_hugetlb_page(tmp)) + reset_vma_resv_huge_pages(tmp); + /* * Link in the new vma and copy the page table entries. */ diff --git a/mm/hugetlb.c b/mm/hugetlb.c index a4dbba8965f3..0af500db3632 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -40,6 +40,69 @@ static int hugetlb_next_nid; */ static DEFINE_SPINLOCK(hugetlb_lock); +/* + * These helpers are used to track how many pages are reserved for + * faults in a MAP_PRIVATE mapping. Only the process that called mmap() + * is guaranteed to have their future faults succeed. + * + * With the exception of reset_vma_resv_huge_pages() which is called at fork(), + * the reserve counters are updated with the hugetlb_lock held. It is safe + * to reset the VMA at fork() time as it is not in use yet and there is no + * chance of the global counters getting corrupted as a result of the values. + */ +static unsigned long vma_resv_huge_pages(struct vm_area_struct *vma) +{ + VM_BUG_ON(!is_vm_hugetlb_page(vma)); + if (!(vma->vm_flags & VM_SHARED)) + return (unsigned long)vma->vm_private_data; + return 0; +} + +static void set_vma_resv_huge_pages(struct vm_area_struct *vma, + unsigned long reserve) +{ + VM_BUG_ON(!is_vm_hugetlb_page(vma)); + VM_BUG_ON(vma->vm_flags & VM_SHARED); + + vma->vm_private_data = (void *)reserve; +} + +/* Decrement the reserved pages in the hugepage pool by one */ +static void decrement_hugepage_resv_vma(struct vm_area_struct *vma) +{ + if (vma->vm_flags & VM_SHARED) { + /* Shared mappings always use reserves */ + resv_huge_pages--; + } else { + /* + * Only the process that called mmap() has reserves for + * private mappings. + */ + if (vma_resv_huge_pages(vma)) { + resv_huge_pages--; + reserve = (unsigned long)vma->vm_private_data - 1; + vma->vm_private_data = (void *)reserve; + } + } +} + +void reset_vma_resv_huge_pages(struct vm_area_struct *vma) +{ + VM_BUG_ON(!is_vm_hugetlb_page(vma)); + if (!(vma->vm_flags & VM_SHARED)) + vma->vm_private_data = (void *)0; +} + +/* Returns true if the VMA has associated reserve pages */ +static int vma_has_private_reserves(struct vm_area_struct *vma) +{ + if (vma->vm_flags & VM_SHARED) + return 0; + if (!vma_resv_huge_pages(vma)) + return 0; + return 1; +} + static void clear_huge_page(struct page *page, unsigned long addr) { int i; @@ -101,6 +164,15 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, struct zone *zone; struct zoneref *z; + /* + * A child process with MAP_PRIVATE mappings created by their parent + * have no page reserves. This check ensures that reservations are + * not "stolen". The child may still get SIGKILLed + */ + if (!vma_has_private_reserves(vma) && + free_huge_pages - resv_huge_pages == 0) + return NULL; + for_each_zone_zonelist_nodemask(zone, z, zonelist, MAX_NR_ZONES - 1, nodemask) { nid = zone_to_nid(zone); @@ -111,8 +183,8 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, list_del(&page->lru); free_huge_pages--; free_huge_pages_node[nid]--; - if (vma && vma->vm_flags & VM_MAYSHARE) - resv_huge_pages--; + decrement_hugepage_resv_vma(vma); + break; } } @@ -461,55 +533,40 @@ static void return_unused_surplus_pages(unsigned long unused_resv_pages) } } - -static struct page *alloc_huge_page_shared(struct vm_area_struct *vma, - unsigned long addr) +static struct page *alloc_huge_page(struct vm_area_struct *vma, + unsigned long addr) { struct page *page; + struct address_space *mapping = vma->vm_file->f_mapping; + struct inode *inode = mapping->host; + unsigned int chg = 0; + + /* + * Processes that did not create the mapping will have no reserves and + * will not have accounted against quota. Check that the quota can be + * made before satisfying the allocation + */ + if (!vma_has_private_reserves(vma)) { + chg = 1; + if (hugetlb_get_quota(inode->i_mapping, chg)) + return ERR_PTR(-ENOSPC); + } spin_lock(&hugetlb_lock); page = dequeue_huge_page_vma(vma, addr); spin_unlock(&hugetlb_lock); - return page ? page : ERR_PTR(-VM_FAULT_OOM); -} -static struct page *alloc_huge_page_private(struct vm_area_struct *vma, - unsigned long addr) -{ - struct page *page = NULL; - - if (hugetlb_get_quota(vma->vm_file->f_mapping, 1)) - return ERR_PTR(-VM_FAULT_SIGBUS); - - spin_lock(&hugetlb_lock); - if (free_huge_pages > resv_huge_pages) - page = dequeue_huge_page_vma(vma, addr); - spin_unlock(&hugetlb_lock); if (!page) { page = alloc_buddy_huge_page(vma, addr); if (!page) { - hugetlb_put_quota(vma->vm_file->f_mapping, 1); + hugetlb_put_quota(inode->i_mapping, chg); return ERR_PTR(-VM_FAULT_OOM); } } - return page; -} -static struct page *alloc_huge_page(struct vm_area_struct *vma, - unsigned long addr) -{ - struct page *page; - struct address_space *mapping = vma->vm_file->f_mapping; - - if (vma->vm_flags & VM_MAYSHARE) - page = alloc_huge_page_shared(vma, addr); - else - page = alloc_huge_page_private(vma, addr); + set_page_refcounted(page); + set_page_private(page, (unsigned long) mapping); - if (!IS_ERR(page)) { - set_page_refcounted(page); - set_page_private(page, (unsigned long) mapping); - } return page; } @@ -757,6 +814,13 @@ out: return ret; } +static void hugetlb_vm_op_close(struct vm_area_struct *vma) +{ + unsigned long reserve = vma_resv_huge_pages(vma); + if (reserve) + hugetlb_acct_memory(-reserve); +} + /* * We cannot handle pagefaults against hugetlb pages at all. They cause * handle_mm_fault() to try to instantiate regular-sized pages in the @@ -771,6 +835,7 @@ static int hugetlb_vm_op_fault(struct vm_area_struct *vma, struct vm_fault *vmf) struct vm_operations_struct hugetlb_vm_ops = { .fault = hugetlb_vm_op_fault, + .close = hugetlb_vm_op_close, }; static pte_t make_huge_pte(struct vm_area_struct *vma, struct page *page, @@ -1289,11 +1354,25 @@ static long region_truncate(struct list_head *head, long end) return chg; } -int hugetlb_reserve_pages(struct inode *inode, long from, long to) +int hugetlb_reserve_pages(struct inode *inode, + long from, long to, + struct vm_area_struct *vma) { long ret, chg; - chg = region_chg(&inode->i_mapping->private_list, from, to); + /* + * Shared mappings base their reservation on the number of pages that + * are already allocated on behalf of the file. Private mappings need + * to reserve the full area even if read-only as mprotect() may be + * called to make the mapping read-write. Assume !vma is a shm mapping + */ + if (!vma || vma->vm_flags & VM_SHARED) + chg = region_chg(&inode->i_mapping->private_list, from, to); + else { + chg = to - from; + set_vma_resv_huge_pages(vma, chg); + } + if (chg < 0) return chg; @@ -1304,7 +1383,8 @@ int hugetlb_reserve_pages(struct inode *inode, long from, long to) hugetlb_put_quota(inode->i_mapping, chg); return ret; } - region_add(&inode->i_mapping->private_list, from, to); + if (!vma || vma->vm_flags & VM_SHARED) + region_add(&inode->i_mapping->private_list, from, to); return 0; } -- cgit From 04f2cbe35699d22dbf428373682ead85ca1240f5 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 23 Jul 2008 21:27:25 -0700 Subject: hugetlb: guarantee that COW faults for a process that called mmap(MAP_PRIVATE) on hugetlbfs will succeed After patch 2 in this series, a process that successfully calls mmap() for a MAP_PRIVATE mapping will be guaranteed to successfully fault until a process calls fork(). At that point, the next write fault from the parent could fail due to COW if the child still has a reference. We only reserve pages for the parent but a copy must be made to avoid leaking data from the parent to the child after fork(). Reserves could be taken for both parent and child at fork time to guarantee faults but if the mapping is large it is highly likely we will not have sufficient pages for the reservation, and it is common to fork only to exec() immediatly after. A failure here would be very undesirable. Note that the current behaviour of mainline with MAP_PRIVATE pages is pretty bad. The following situation is allowed to occur today. 1. Process calls mmap(MAP_PRIVATE) 2. Process calls mlock() to fault all pages and makes sure it succeeds 3. Process forks() 4. Process writes to MAP_PRIVATE mapping while child still exists 5. If the COW fails at this point, the process gets SIGKILLed even though it had taken care to ensure the pages existed This patch improves the situation by guaranteeing the reliability of the process that successfully calls mmap(). When the parent performs COW, it will try to satisfy the allocation without using reserves. If that fails the parent will steal the page leaving any children without a page. Faults from the child after that point will result in failure. If the child COW happens first, an attempt will be made to allocate the page without reserves and the child will get SIGKILLed on failure. To summarise the new behaviour: 1. If the original mapper performs COW on a private mapping with multiple references, it will attempt to allocate a hugepage from the pool or the buddy allocator without using the existing reserves. On fail, VMAs mapping the same area are traversed and the page being COW'd is unmapped where found. It will then steal the original page as the last mapper in the normal way. 2. The VMAs the pages were unmapped from are flagged to note that pages with data no longer exist. Future no-page faults on those VMAs will terminate the process as otherwise it would appear that data was corrupted. A warning is printed to the console that this situation occured. 2. If the child performs COW first, it will attempt to satisfy the COW from the pool if there are enough pages or via the buddy allocator if overcommit is allowed and the buddy allocator can satisfy the request. If it fails, the child will be killed. If the pool is large enough, existing applications will not notice that the reserves were a factor. Existing applications depending on the no-reserves been set are unlikely to exist as for much of the history of hugetlbfs, pages were prefaulted at mmap(), allocating the pages at that point or failing the mmap(). [npiggin@suse.de: fix CONFIG_HUGETLB=n build] Signed-off-by: Mel Gorman Acked-by: Adam Litke Cc: Andy Whitcroft Cc: William Lee Irwin III Cc: Hugh Dickins Cc: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/hugetlbfs/inode.c | 2 +- include/linux/hugetlb.h | 8 +- mm/hugetlb.c | 201 +++++++++++++++++++++++++++++++++++++++++++----- mm/memory.c | 2 +- 4 files changed, 190 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 1576bbecd084..428eff5b73f3 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -441,7 +441,7 @@ hugetlb_vmtruncate_list(struct prio_tree_root *root, pgoff_t pgoff) v_offset = 0; __unmap_hugepage_range(vma, - vma->vm_start + v_offset, vma->vm_end); + vma->vm_start + v_offset, vma->vm_end, NULL); } } diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 185b14c9f021..abbc187193a1 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -23,8 +23,10 @@ int hugetlb_overcommit_handler(struct ctl_table *, int, struct file *, void __us int hugetlb_treat_movable_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *); int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, struct page **, struct vm_area_struct **, unsigned long *, int *, int, int); -void unmap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned long); -void __unmap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned long); +void unmap_hugepage_range(struct vm_area_struct *, + unsigned long, unsigned long, struct page *); +void __unmap_hugepage_range(struct vm_area_struct *, + unsigned long, unsigned long, struct page *); int hugetlb_prefault(struct address_space *, struct vm_area_struct *); int hugetlb_report_meminfo(char *); int hugetlb_report_node_meminfo(int, char *); @@ -74,7 +76,7 @@ static inline unsigned long hugetlb_total_pages(void) #define follow_huge_addr(mm, addr, write) ERR_PTR(-EINVAL) #define copy_hugetlb_page_range(src, dst, vma) ({ BUG(); 0; }) #define hugetlb_prefault(mapping, vma) ({ BUG(); 0; }) -#define unmap_hugepage_range(vma, start, end) BUG() +#define unmap_hugepage_range(vma, start, end, page) BUG() #define hugetlb_report_meminfo(buf) 0 #define hugetlb_report_node_meminfo(n, buf) 0 #define follow_huge_pmd(mm, addr, pmd, write) NULL diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 0af500db3632..a2d29b84501f 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -40,6 +40,9 @@ static int hugetlb_next_nid; */ static DEFINE_SPINLOCK(hugetlb_lock); +#define HPAGE_RESV_OWNER (1UL << (BITS_PER_LONG - 1)) +#define HPAGE_RESV_UNMAPPED (1UL << (BITS_PER_LONG - 2)) +#define HPAGE_RESV_MASK (HPAGE_RESV_OWNER | HPAGE_RESV_UNMAPPED) /* * These helpers are used to track how many pages are reserved for * faults in a MAP_PRIVATE mapping. Only the process that called mmap() @@ -54,17 +57,32 @@ static unsigned long vma_resv_huge_pages(struct vm_area_struct *vma) { VM_BUG_ON(!is_vm_hugetlb_page(vma)); if (!(vma->vm_flags & VM_SHARED)) - return (unsigned long)vma->vm_private_data; + return (unsigned long)vma->vm_private_data & ~HPAGE_RESV_MASK; return 0; } static void set_vma_resv_huge_pages(struct vm_area_struct *vma, unsigned long reserve) { + unsigned long flags; VM_BUG_ON(!is_vm_hugetlb_page(vma)); VM_BUG_ON(vma->vm_flags & VM_SHARED); - vma->vm_private_data = (void *)reserve; + flags = (unsigned long)vma->vm_private_data & HPAGE_RESV_MASK; + vma->vm_private_data = (void *)(reserve | flags); +} + +static void set_vma_resv_flags(struct vm_area_struct *vma, unsigned long flags) +{ + unsigned long reserveflags = (unsigned long)vma->vm_private_data; + VM_BUG_ON(!is_vm_hugetlb_page(vma)); + vma->vm_private_data = (void *)(reserveflags | flags); +} + +static int is_vma_resv_set(struct vm_area_struct *vma, unsigned long flag) +{ + VM_BUG_ON(!is_vm_hugetlb_page(vma)); + return ((unsigned long)vma->vm_private_data & flag) != 0; } /* Decrement the reserved pages in the hugepage pool by one */ @@ -78,14 +96,18 @@ static void decrement_hugepage_resv_vma(struct vm_area_struct *vma) * Only the process that called mmap() has reserves for * private mappings. */ - if (vma_resv_huge_pages(vma)) { + if (is_vma_resv_set(vma, HPAGE_RESV_OWNER)) { + unsigned long flags, reserve; resv_huge_pages--; + flags = (unsigned long)vma->vm_private_data & + HPAGE_RESV_MASK; reserve = (unsigned long)vma->vm_private_data - 1; - vma->vm_private_data = (void *)reserve; + vma->vm_private_data = (void *)(reserve | flags); } } } +/* Reset counters to 0 and clear all HPAGE_RESV_* flags */ void reset_vma_resv_huge_pages(struct vm_area_struct *vma) { VM_BUG_ON(!is_vm_hugetlb_page(vma)); @@ -153,7 +175,7 @@ static struct page *dequeue_huge_page(void) } static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, - unsigned long address) + unsigned long address, int avoid_reserve) { int nid; struct page *page = NULL; @@ -173,6 +195,10 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, free_huge_pages - resv_huge_pages == 0) return NULL; + /* If reserves cannot be used, ensure enough pages are in the pool */ + if (avoid_reserve && free_huge_pages - resv_huge_pages == 0) + return NULL; + for_each_zone_zonelist_nodemask(zone, z, zonelist, MAX_NR_ZONES - 1, nodemask) { nid = zone_to_nid(zone); @@ -183,7 +209,9 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, list_del(&page->lru); free_huge_pages--; free_huge_pages_node[nid]--; - decrement_hugepage_resv_vma(vma); + + if (!avoid_reserve) + decrement_hugepage_resv_vma(vma); break; } @@ -534,7 +562,7 @@ static void return_unused_surplus_pages(unsigned long unused_resv_pages) } static struct page *alloc_huge_page(struct vm_area_struct *vma, - unsigned long addr) + unsigned long addr, int avoid_reserve) { struct page *page; struct address_space *mapping = vma->vm_file->f_mapping; @@ -546,14 +574,15 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, * will not have accounted against quota. Check that the quota can be * made before satisfying the allocation */ - if (!vma_has_private_reserves(vma)) { + if (!(vma->vm_flags & VM_SHARED) && + !is_vma_resv_set(vma, HPAGE_RESV_OWNER)) { chg = 1; if (hugetlb_get_quota(inode->i_mapping, chg)) return ERR_PTR(-ENOSPC); } spin_lock(&hugetlb_lock); - page = dequeue_huge_page_vma(vma, addr); + page = dequeue_huge_page_vma(vma, addr, avoid_reserve); spin_unlock(&hugetlb_lock); if (!page) { @@ -909,7 +938,7 @@ nomem: } void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end) + unsigned long end, struct page *ref_page) { struct mm_struct *mm = vma->vm_mm; unsigned long address; @@ -937,6 +966,27 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, if (huge_pmd_unshare(mm, &address, ptep)) continue; + /* + * If a reference page is supplied, it is because a specific + * page is being unmapped, not a range. Ensure the page we + * are about to unmap is the actual page of interest. + */ + if (ref_page) { + pte = huge_ptep_get(ptep); + if (huge_pte_none(pte)) + continue; + page = pte_page(pte); + if (page != ref_page) + continue; + + /* + * Mark the VMA as having unmapped its page so that + * future faults in this VMA will fail rather than + * looking like data was lost + */ + set_vma_resv_flags(vma, HPAGE_RESV_UNMAPPED); + } + pte = huge_ptep_get_and_clear(mm, address, ptep); if (huge_pte_none(pte)) continue; @@ -955,7 +1005,7 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, } void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end) + unsigned long end, struct page *ref_page) { /* * It is undesirable to test vma->vm_file as it should be non-null @@ -967,19 +1017,68 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, */ if (vma->vm_file) { spin_lock(&vma->vm_file->f_mapping->i_mmap_lock); - __unmap_hugepage_range(vma, start, end); + __unmap_hugepage_range(vma, start, end, ref_page); spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock); } } +/* + * This is called when the original mapper is failing to COW a MAP_PRIVATE + * mappping it owns the reserve page for. The intention is to unmap the page + * from other VMAs and let the children be SIGKILLed if they are faulting the + * same region. + */ +int unmap_ref_private(struct mm_struct *mm, + struct vm_area_struct *vma, + struct page *page, + unsigned long address) +{ + struct vm_area_struct *iter_vma; + struct address_space *mapping; + struct prio_tree_iter iter; + pgoff_t pgoff; + + /* + * vm_pgoff is in PAGE_SIZE units, hence the different calculation + * from page cache lookup which is in HPAGE_SIZE units. + */ + address = address & huge_page_mask(hstate_vma(vma)); + pgoff = ((address - vma->vm_start) >> PAGE_SHIFT) + + (vma->vm_pgoff >> PAGE_SHIFT); + mapping = (struct address_space *)page_private(page); + + vma_prio_tree_foreach(iter_vma, &iter, &mapping->i_mmap, pgoff, pgoff) { + /* Do not unmap the current VMA */ + if (iter_vma == vma) + continue; + + /* + * Unmap the page from other VMAs without their own reserves. + * They get marked to be SIGKILLed if they fault in these + * areas. This is because a future no-page fault on this VMA + * could insert a zeroed page instead of the data existing + * from the time of fork. This would look like data corruption + */ + if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER)) + unmap_hugepage_range(iter_vma, + address, address + HPAGE_SIZE, + page); + } + + return 1; +} + static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long address, pte_t *ptep, pte_t pte) + unsigned long address, pte_t *ptep, pte_t pte, + struct page *pagecache_page) { struct page *old_page, *new_page; int avoidcopy; + int outside_reserve = 0; old_page = pte_page(pte); +retry_avoidcopy: /* If no-one else is actually using this page, avoid the copy * and just make the page writable */ avoidcopy = (page_count(old_page) == 1); @@ -988,11 +1087,43 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, return 0; } + /* + * If the process that created a MAP_PRIVATE mapping is about to + * perform a COW due to a shared page count, attempt to satisfy + * the allocation without using the existing reserves. The pagecache + * page is used to determine if the reserve at this address was + * consumed or not. If reserves were used, a partial faulted mapping + * at the time of fork() could consume its reserves on COW instead + * of the full address range. + */ + if (!(vma->vm_flags & VM_SHARED) && + is_vma_resv_set(vma, HPAGE_RESV_OWNER) && + old_page != pagecache_page) + outside_reserve = 1; + page_cache_get(old_page); - new_page = alloc_huge_page(vma, address); + new_page = alloc_huge_page(vma, address, outside_reserve); if (IS_ERR(new_page)) { page_cache_release(old_page); + + /* + * If a process owning a MAP_PRIVATE mapping fails to COW, + * it is due to references held by a child and an insufficient + * huge page pool. To guarantee the original mappers + * reliability, unmap the page from child processes. The child + * may get SIGKILLed if it later faults. + */ + if (outside_reserve) { + BUG_ON(huge_pte_none(pte)); + if (unmap_ref_private(mm, vma, old_page, address)) { + BUG_ON(page_count(old_page) != 1); + BUG_ON(huge_pte_none(pte)); + goto retry_avoidcopy; + } + WARN_ON_ONCE(1); + } + return -PTR_ERR(new_page); } @@ -1015,6 +1146,20 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, return 0; } +/* Return the pagecache page at a given address within a VMA */ +static struct page *hugetlbfs_pagecache_page(struct vm_area_struct *vma, + unsigned long address) +{ + struct address_space *mapping; + unsigned long idx; + + mapping = vma->vm_file->f_mapping; + idx = ((address - vma->vm_start) >> HPAGE_SHIFT) + + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT)); + + return find_lock_page(mapping, idx); +} + static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, pte_t *ptep, int write_access) { @@ -1025,6 +1170,18 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, struct address_space *mapping; pte_t new_pte; + /* + * Currently, we are forced to kill the process in the event the + * original mapper has unmapped pages from the child due to a failed + * COW. Warn that such a situation has occured as it may not be obvious + */ + if (is_vma_resv_set(vma, HPAGE_RESV_UNMAPPED)) { + printk(KERN_WARNING + "PID %d killed due to inadequate hugepage pool\n", + current->pid); + return ret; + } + mapping = vma->vm_file->f_mapping; idx = ((address - vma->vm_start) >> HPAGE_SHIFT) + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT)); @@ -1039,7 +1196,7 @@ retry: size = i_size_read(mapping->host) >> HPAGE_SHIFT; if (idx >= size) goto out; - page = alloc_huge_page(vma, address); + page = alloc_huge_page(vma, address, 0); if (IS_ERR(page)) { ret = -PTR_ERR(page); goto out; @@ -1081,7 +1238,7 @@ retry: if (write_access && !(vma->vm_flags & VM_SHARED)) { /* Optimization, do the COW without a second fault */ - ret = hugetlb_cow(mm, vma, address, ptep, new_pte); + ret = hugetlb_cow(mm, vma, address, ptep, new_pte, page); } spin_unlock(&mm->page_table_lock); @@ -1126,8 +1283,15 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, spin_lock(&mm->page_table_lock); /* Check for a racing update before calling hugetlb_cow */ if (likely(pte_same(entry, huge_ptep_get(ptep)))) - if (write_access && !pte_write(entry)) - ret = hugetlb_cow(mm, vma, address, ptep, entry); + if (write_access && !pte_write(entry)) { + struct page *page; + page = hugetlbfs_pagecache_page(vma, address); + ret = hugetlb_cow(mm, vma, address, ptep, entry, page); + if (page) { + unlock_page(page); + put_page(page); + } + } spin_unlock(&mm->page_table_lock); mutex_unlock(&hugetlb_instantiation_mutex); @@ -1371,6 +1535,7 @@ int hugetlb_reserve_pages(struct inode *inode, else { chg = to - from; set_vma_resv_huge_pages(vma, chg); + set_vma_resv_flags(vma, HPAGE_RESV_OWNER); } if (chg < 0) diff --git a/mm/memory.c b/mm/memory.c index 82f3f1c5cf17..72932489a082 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -901,7 +901,7 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, } if (unlikely(is_vm_hugetlb_page(vma))) { - unmap_hugepage_range(vma, start, end); + unmap_hugepage_range(vma, start, end, NULL); zap_work -= (end - start) / (HPAGE_SIZE / PAGE_SIZE); start = end; -- cgit From cdfd4325c0d878679bd6a3ba8285b71d9980e3c0 Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Wed, 23 Jul 2008 21:27:28 -0700 Subject: mm: record MAP_NORESERVE status on vmas and fix small page mprotect reservations With Mel's hugetlb private reservation support patches applied, strict overcommit semantics are applied to both shared and private huge page mappings. This can be a problem if an application relied on unlimited overcommit semantics for private mappings. An example of this would be an application which maps a huge area with the intention of using it very sparsely. These application would benefit from being able to opt-out of the strict overcommit. It should be noted that prior to hugetlb supporting demand faulting all mappings were fully populated and so applications of this type should be rare. This patch stack implements the MAP_NORESERVE mmap() flag for huge page mappings. This flag has the same meaning as for small page mappings, suppressing reservations for that mapping. Thanks to Mel Gorman for reviewing a number of early versions of these patches. This patch: When a small page mapping is created with mmap() reservations are created by default for any memory pages required. When the region is read/write the reservation is increased for every page, no reservation is needed for read-only regions (as they implicitly share the zero page). Reservations are tracked via the VM_ACCOUNT vma flag which is present when the region has reservation backing it. When we convert a region from read-only to read-write new reservations are aquired and VM_ACCOUNT is set. However, when a read-only map is created with MAP_NORESERVE it is indistinguishable from a normal mapping. When we then convert that to read/write we are forced to incorrectly create reservations for it as we have no record of the original MAP_NORESERVE. This patch introduces a new vma flag VM_NORESERVE which records the presence of the original MAP_NORESERVE flag. This allows us to distinguish these two circumstances and correctly account the reserve. As well as fixing this FIXME in the code, this makes it much easier to introduce MAP_NORESERVE support for huge pages as this flag is available consistantly for the life of the mapping. VM_ACCOUNT on the other hand is heavily used at the generic level in association with small pages. Signed-off-by: Andy Whitcroft Cc: Mel Gorman Cc: Adam Litke Cc: Johannes Weiner Cc: Andy Whitcroft Cc: William Lee Irwin III Cc: Hugh Dickins Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 1 + mm/mmap.c | 3 +++ mm/mprotect.c | 6 ++---- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 196924b657bc..df322fb4df31 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -100,6 +100,7 @@ extern unsigned int kobjsize(const void *objp); #define VM_DONTEXPAND 0x00040000 /* Cannot expand with mremap() */ #define VM_RESERVED 0x00080000 /* Count as reserved_vm like IO */ #define VM_ACCOUNT 0x00100000 /* Is a VM accounted object */ +#define VM_NORESERVE 0x00200000 /* should the VM suppress accounting */ #define VM_HUGETLB 0x00400000 /* Huge TLB Page VM */ #define VM_NONLINEAR 0x00800000 /* Is non-linear (remap_file_pages) */ #define VM_MAPPED_COPY 0x01000000 /* T if mapped copy of data (nommu mmap) */ diff --git a/mm/mmap.c b/mm/mmap.c index 75e0d0673d78..57d3b6097deb 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1110,6 +1110,9 @@ munmap_back: if (!may_expand_vm(mm, len >> PAGE_SHIFT)) return -ENOMEM; + if (flags & MAP_NORESERVE) + vm_flags |= VM_NORESERVE; + if (accountable && (!(flags & MAP_NORESERVE) || sysctl_overcommit_memory == OVERCOMMIT_NEVER)) { if (vm_flags & VM_SHARED) { diff --git a/mm/mprotect.c b/mm/mprotect.c index 360d9cc8b38c..abd645a3b0a0 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -153,12 +153,10 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, * If we make a private mapping writable we increase our commit; * but (without finer accounting) cannot reduce our commit if we * make it unwritable again. - * - * FIXME? We haven't defined a VM_NORESERVE flag, so mprotecting - * a MAP_NORESERVE private mapping to writable will now reserve. */ if (newflags & VM_WRITE) { - if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_SHARED))) { + if (!(oldflags & (VM_ACCOUNT|VM_WRITE| + VM_SHARED|VM_NORESERVE))) { charged = nrpages; if (security_vm_enough_memory(charged)) return -ENOMEM; -- cgit From ff7ea79cf7c3a481851bd4b2185fdeb6ce4afa29 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 23 Jul 2008 21:27:39 -0700 Subject: mm: create /sys/kernel/mm Add a kobject to create /sys/kernel/mm when sysfs is mounted. The kobject will exist regardless. This will allow for the hugepage related sysfs directories to exist under the mm "subsystem" directory. Add an ABI file appropriately. [kosaki.motohiro@jp.fujitsu.com: fix build] Signed-off-by: Nishanth Aravamudan Cc: Nick Piggin Cc: Mel Gorman Signed-off-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ABI/testing/sysfs-kernel-mm | 6 ++++++ include/linux/kobject.h | 2 ++ mm/mm_init.c | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-kernel-mm (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-kernel-mm b/Documentation/ABI/testing/sysfs-kernel-mm new file mode 100644 index 000000000000..190d523ac159 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-mm @@ -0,0 +1,6 @@ +What: /sys/kernel/mm +Date: July 2008 +Contact: Nishanth Aravamudan , VM maintainers +Description: + /sys/kernel/mm/ should contain any and all VM + related information in /sys/kernel/. diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 60f0d418ae32..5437ac0276e2 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -186,6 +186,8 @@ extern struct kobject *kset_find_obj(struct kset *, const char *); /* The global /sys/kernel/ kobject for people to chain off of */ extern struct kobject *kernel_kobj; +/* The global /sys/kernel/mm/ kobject for people to chain off of */ +extern struct kobject *mm_kobj; /* The global /sys/hypervisor/ kobject for people to chain off of */ extern struct kobject *hypervisor_kobj; /* The global /sys/power/ kobject for people to chain off of */ diff --git a/mm/mm_init.c b/mm/mm_init.c index eaf0d3b47099..c6af41ea9994 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -7,6 +7,8 @@ */ #include #include +#include +#include #include "internal.h" #ifdef CONFIG_DEBUG_MEMORY_INIT @@ -134,3 +136,17 @@ static __init int set_mminit_loglevel(char *str) } early_param("mminit_loglevel", set_mminit_loglevel); #endif /* CONFIG_DEBUG_MEMORY_INIT */ + +struct kobject *mm_kobj; +EXPORT_SYMBOL_GPL(mm_kobj); + +static int __init mm_sysfs_init(void) +{ + mm_kobj = kobject_create_and_add("mm", kernel_kobj); + if (!mm_kobj) + return -ENOMEM; + + return 0; +} + +__initcall(mm_sysfs_init); -- cgit From a5516438959d90b071ff0a484ce4f3f523dc3152 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 23 Jul 2008 21:27:41 -0700 Subject: hugetlb: modular state for hugetlb page size The goal of this patchset is to support multiple hugetlb page sizes. This is achieved by introducing a new struct hstate structure, which encapsulates the important hugetlb state and constants (eg. huge page size, number of huge pages currently allocated, etc). The hstate structure is then passed around the code which requires these fields, they will do the right thing regardless of the exact hstate they are operating on. This patch adds the hstate structure, with a single global instance of it (default_hstate), and does the basic work of converting hugetlb to use the hstate. Future patches will add more hstate structures to allow for different hugetlbfs mounts to have different page sizes. [akpm@linux-foundation.org: coding-style fixes] Acked-by: Adam Litke Acked-by: Nishanth Aravamudan Signed-off-by: Andi Kleen Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/mm/hugetlbpage.c | 7 +- arch/powerpc/mm/hugetlbpage.c | 3 +- arch/s390/mm/hugetlbpage.c | 3 +- arch/sh/mm/hugetlbpage.c | 3 +- arch/sparc64/mm/hugetlbpage.c | 5 +- arch/x86/mm/hugetlbpage.c | 5 +- fs/hugetlbfs/inode.c | 52 +++--- include/asm-ia64/hugetlb.h | 3 +- include/asm-powerpc/hugetlb.h | 3 +- include/asm-s390/hugetlb.h | 3 +- include/asm-sh/hugetlb.h | 3 +- include/asm-sparc/hugetlb.h | 3 +- include/asm-x86/hugetlb.h | 8 +- include/linux/hugetlb.h | 88 +++++++++- ipc/shm.c | 3 +- mm/hugetlb.c | 368 +++++++++++++++++++++++------------------- mm/memory.c | 2 +- mm/mempolicy.c | 9 +- mm/mmap.c | 3 +- 19 files changed, 356 insertions(+), 218 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/mm/hugetlbpage.c b/arch/ia64/mm/hugetlbpage.c index cd49e2860eef..6170f097d255 100644 --- a/arch/ia64/mm/hugetlbpage.c +++ b/arch/ia64/mm/hugetlbpage.c @@ -24,7 +24,7 @@ unsigned int hpage_shift=HPAGE_SHIFT_DEFAULT; pte_t * -huge_pte_alloc (struct mm_struct *mm, unsigned long addr) +huge_pte_alloc(struct mm_struct *mm, unsigned long addr, unsigned long sz) { unsigned long taddr = htlbpage_to_page(addr); pgd_t *pgd; @@ -75,7 +75,8 @@ int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) * Don't actually need to do any preparation, but need to make sure * the address is in the right region. */ -int prepare_hugepage_range(unsigned long addr, unsigned long len) +int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; @@ -149,7 +150,7 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, u /* Handle MAP_FIXED */ if (flags & MAP_FIXED) { - if (prepare_hugepage_range(addr, len)) + if (prepare_hugepage_range(file, addr, len)) return -EINVAL; return addr; } diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 1a96cc891cf5..c94dc71af989 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -128,7 +128,8 @@ pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) return NULL; } -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pg; pud_t *pu; diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index f4b6124fdb75..9162dc84f77f 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -72,7 +72,8 @@ void arch_release_hugepage(struct page *page) page[1].index = 0; } -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pgdp; pud_t *pudp; diff --git a/arch/sh/mm/hugetlbpage.c b/arch/sh/mm/hugetlbpage.c index ae8c321d6e2a..2f9dbe0ef4ac 100644 --- a/arch/sh/mm/hugetlbpage.c +++ b/arch/sh/mm/hugetlbpage.c @@ -22,7 +22,8 @@ #include #include -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pgd; pud_t *pud; diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c index ebefd2a14375..1307b23f6a76 100644 --- a/arch/sparc64/mm/hugetlbpage.c +++ b/arch/sparc64/mm/hugetlbpage.c @@ -175,7 +175,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, return -ENOMEM; if (flags & MAP_FIXED) { - if (prepare_hugepage_range(addr, len)) + if (prepare_hugepage_range(file, addr, len)) return -EINVAL; return addr; } @@ -195,7 +195,8 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, pgoff, flags); } -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pgd; pud_t *pud; diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c index 0b3d567e686d..52476fde8996 100644 --- a/arch/x86/mm/hugetlbpage.c +++ b/arch/x86/mm/hugetlbpage.c @@ -124,7 +124,8 @@ int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) return 1; } -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pgd; pud_t *pud; @@ -368,7 +369,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, return -ENOMEM; if (flags & MAP_FIXED) { - if (prepare_hugepage_range(addr, len)) + if (prepare_hugepage_range(file, addr, len)) return -EINVAL; return addr; } diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 428eff5b73f3..516c581b5371 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -80,6 +80,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) struct inode *inode = file->f_path.dentry->d_inode; loff_t len, vma_len; int ret; + struct hstate *h = hstate_file(file); /* * vma address alignment (but not the pgoff alignment) has @@ -92,7 +93,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) vma->vm_flags |= VM_HUGETLB | VM_RESERVED; vma->vm_ops = &hugetlb_vm_ops; - if (vma->vm_pgoff & ~(HPAGE_MASK >> PAGE_SHIFT)) + if (vma->vm_pgoff & ~(huge_page_mask(h) >> PAGE_SHIFT)) return -EINVAL; vma_len = (loff_t)(vma->vm_end - vma->vm_start); @@ -104,8 +105,8 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); if (hugetlb_reserve_pages(inode, - vma->vm_pgoff >> (HPAGE_SHIFT-PAGE_SHIFT), - len >> HPAGE_SHIFT, vma)) + vma->vm_pgoff >> huge_page_order(h), + len >> huge_page_shift(h), vma)) goto out; ret = 0; @@ -130,20 +131,21 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, struct mm_struct *mm = current->mm; struct vm_area_struct *vma; unsigned long start_addr; + struct hstate *h = hstate_file(file); - if (len & ~HPAGE_MASK) + if (len & ~huge_page_mask(h)) return -EINVAL; if (len > TASK_SIZE) return -ENOMEM; if (flags & MAP_FIXED) { - if (prepare_hugepage_range(addr, len)) + if (prepare_hugepage_range(file, addr, len)) return -EINVAL; return addr; } if (addr) { - addr = ALIGN(addr, HPAGE_SIZE); + addr = ALIGN(addr, huge_page_size(h)); vma = find_vma(mm, addr); if (TASK_SIZE - len >= addr && (!vma || addr + len <= vma->vm_start)) @@ -156,7 +158,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, start_addr = TASK_UNMAPPED_BASE; full_search: - addr = ALIGN(start_addr, HPAGE_SIZE); + addr = ALIGN(start_addr, huge_page_size(h)); for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { /* At this point: (!vma || addr < vma->vm_end). */ @@ -174,7 +176,7 @@ full_search: if (!vma || addr + len <= vma->vm_start) return addr; - addr = ALIGN(vma->vm_end, HPAGE_SIZE); + addr = ALIGN(vma->vm_end, huge_page_size(h)); } } #endif @@ -225,10 +227,11 @@ hugetlbfs_read_actor(struct page *page, unsigned long offset, static ssize_t hugetlbfs_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { + struct hstate *h = hstate_file(filp); struct address_space *mapping = filp->f_mapping; struct inode *inode = mapping->host; - unsigned long index = *ppos >> HPAGE_SHIFT; - unsigned long offset = *ppos & ~HPAGE_MASK; + unsigned long index = *ppos >> huge_page_shift(h); + unsigned long offset = *ppos & ~huge_page_mask(h); unsigned long end_index; loff_t isize; ssize_t retval = 0; @@ -243,17 +246,17 @@ static ssize_t hugetlbfs_read(struct file *filp, char __user *buf, if (!isize) goto out; - end_index = (isize - 1) >> HPAGE_SHIFT; + end_index = (isize - 1) >> huge_page_shift(h); for (;;) { struct page *page; - int nr, ret; + unsigned long nr, ret; /* nr is the maximum number of bytes to copy from this page */ - nr = HPAGE_SIZE; + nr = huge_page_size(h); if (index >= end_index) { if (index > end_index) goto out; - nr = ((isize - 1) & ~HPAGE_MASK) + 1; + nr = ((isize - 1) & ~huge_page_mask(h)) + 1; if (nr <= offset) { goto out; } @@ -287,8 +290,8 @@ static ssize_t hugetlbfs_read(struct file *filp, char __user *buf, offset += ret; retval += ret; len -= ret; - index += offset >> HPAGE_SHIFT; - offset &= ~HPAGE_MASK; + index += offset >> huge_page_shift(h); + offset &= ~huge_page_mask(h); if (page) page_cache_release(page); @@ -298,7 +301,7 @@ static ssize_t hugetlbfs_read(struct file *filp, char __user *buf, break; } out: - *ppos = ((loff_t)index << HPAGE_SHIFT) + offset; + *ppos = ((loff_t)index << huge_page_shift(h)) + offset; mutex_unlock(&inode->i_mutex); return retval; } @@ -339,8 +342,9 @@ static void truncate_huge_page(struct page *page) static void truncate_hugepages(struct inode *inode, loff_t lstart) { + struct hstate *h = hstate_inode(inode); struct address_space *mapping = &inode->i_data; - const pgoff_t start = lstart >> HPAGE_SHIFT; + const pgoff_t start = lstart >> huge_page_shift(h); struct pagevec pvec; pgoff_t next; int i, freed = 0; @@ -449,8 +453,9 @@ static int hugetlb_vmtruncate(struct inode *inode, loff_t offset) { pgoff_t pgoff; struct address_space *mapping = inode->i_mapping; + struct hstate *h = hstate_inode(inode); - BUG_ON(offset & ~HPAGE_MASK); + BUG_ON(offset & ~huge_page_mask(h)); pgoff = offset >> PAGE_SHIFT; i_size_write(inode, offset); @@ -465,6 +470,7 @@ static int hugetlb_vmtruncate(struct inode *inode, loff_t offset) static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; + struct hstate *h = hstate_inode(inode); int error; unsigned int ia_valid = attr->ia_valid; @@ -476,7 +482,7 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) if (ia_valid & ATTR_SIZE) { error = -EINVAL; - if (!(attr->ia_size & ~HPAGE_MASK)) + if (!(attr->ia_size & ~huge_page_mask(h))) error = hugetlb_vmtruncate(inode, attr->ia_size); if (error) goto out; @@ -610,9 +616,10 @@ static int hugetlbfs_set_page_dirty(struct page *page) static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(dentry->d_sb); + struct hstate *h = hstate_inode(dentry->d_inode); buf->f_type = HUGETLBFS_MAGIC; - buf->f_bsize = HPAGE_SIZE; + buf->f_bsize = huge_page_size(h); if (sbinfo) { spin_lock(&sbinfo->stat_lock); /* If no limits set, just report 0 for max/free/used @@ -942,7 +949,8 @@ struct file *hugetlb_file_setup(const char *name, size_t size) goto out_dentry; error = -ENOMEM; - if (hugetlb_reserve_pages(inode, 0, size >> HPAGE_SHIFT, NULL)) + if (hugetlb_reserve_pages(inode, 0, + size >> huge_page_shift(hstate_inode(inode)), NULL)) goto out_inode; d_instantiate(dentry, inode); diff --git a/include/asm-ia64/hugetlb.h b/include/asm-ia64/hugetlb.h index e9d1e5e2382d..da55c63728e0 100644 --- a/include/asm-ia64/hugetlb.h +++ b/include/asm-ia64/hugetlb.h @@ -8,7 +8,8 @@ void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling); -int prepare_hugepage_range(unsigned long addr, unsigned long len); +int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len); static inline int is_hugepage_only_range(struct mm_struct *mm, unsigned long addr, diff --git a/include/asm-powerpc/hugetlb.h b/include/asm-powerpc/hugetlb.h index 0a37aa5ecaa5..ca37c4af27b1 100644 --- a/include/asm-powerpc/hugetlb.h +++ b/include/asm-powerpc/hugetlb.h @@ -21,7 +21,8 @@ pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. */ -static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; diff --git a/include/asm-s390/hugetlb.h b/include/asm-s390/hugetlb.h index 600a776f8f75..670a1d1745d2 100644 --- a/include/asm-s390/hugetlb.h +++ b/include/asm-s390/hugetlb.h @@ -22,7 +22,8 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. */ -static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; diff --git a/include/asm-sh/hugetlb.h b/include/asm-sh/hugetlb.h index fb30018938c7..967068fb79ac 100644 --- a/include/asm-sh/hugetlb.h +++ b/include/asm-sh/hugetlb.h @@ -14,7 +14,8 @@ static inline int is_hugepage_only_range(struct mm_struct *mm, * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. */ -static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; diff --git a/include/asm-sparc/hugetlb.h b/include/asm-sparc/hugetlb.h index aeb92374ca3d..177061064ee6 100644 --- a/include/asm-sparc/hugetlb.h +++ b/include/asm-sparc/hugetlb.h @@ -22,7 +22,8 @@ static inline int is_hugepage_only_range(struct mm_struct *mm, * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. */ -static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { if (len & ~HPAGE_MASK) return -EINVAL; diff --git a/include/asm-x86/hugetlb.h b/include/asm-x86/hugetlb.h index 7eed6e0883bf..439a9acc132d 100644 --- a/include/asm-x86/hugetlb.h +++ b/include/asm-x86/hugetlb.h @@ -14,11 +14,13 @@ static inline int is_hugepage_only_range(struct mm_struct *mm, * If the arch doesn't supply something else, assume that hugepage * size aligned regions are ok without further preparation. */ -static inline int prepare_hugepage_range(unsigned long addr, unsigned long len) +static inline int prepare_hugepage_range(struct file *file, + unsigned long addr, unsigned long len) { - if (len & ~HPAGE_MASK) + struct hstate *h = hstate_file(file); + if (len & ~huge_page_mask(h)) return -EINVAL; - if (addr & ~HPAGE_MASK) + if (addr & ~huge_page_mask(h)) return -EINVAL; return 0; } diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index abbc187193a1..ad2271e11f9b 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -8,7 +8,6 @@ #include #include #include -#include struct ctl_table; @@ -45,7 +44,8 @@ extern int sysctl_hugetlb_shm_group; /* arch callbacks */ -pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr); +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz); pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr); int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep); struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, @@ -80,7 +80,7 @@ static inline unsigned long hugetlb_total_pages(void) #define hugetlb_report_meminfo(buf) 0 #define hugetlb_report_node_meminfo(n, buf) 0 #define follow_huge_pmd(mm, addr, pmd, write) NULL -#define prepare_hugepage_range(addr,len) (-EINVAL) +#define prepare_hugepage_range(file, addr, len) (-EINVAL) #define pmd_huge(x) 0 #define is_hugepage_only_range(mm, addr, len) 0 #define hugetlb_free_pgd_range(tlb, addr, end, floor, ceiling) ({BUG(); 0; }) @@ -134,8 +134,6 @@ struct file *hugetlb_file_setup(const char *name, size_t); int hugetlb_get_quota(struct address_space *mapping, long delta); void hugetlb_put_quota(struct address_space *mapping, long delta); -#define BLOCKS_PER_HUGEPAGE (HPAGE_SIZE / 512) - static inline int is_file_hugepages(struct file *file) { if (file->f_op == &hugetlbfs_file_operations) @@ -164,4 +162,84 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long flags); #endif /* HAVE_ARCH_HUGETLB_UNMAPPED_AREA */ +#ifdef CONFIG_HUGETLB_PAGE + +/* Defines one hugetlb page size */ +struct hstate { + int hugetlb_next_nid; + unsigned int order; + unsigned long mask; + unsigned long max_huge_pages; + unsigned long nr_huge_pages; + unsigned long free_huge_pages; + unsigned long resv_huge_pages; + unsigned long surplus_huge_pages; + unsigned long nr_overcommit_huge_pages; + struct list_head hugepage_freelists[MAX_NUMNODES]; + unsigned int nr_huge_pages_node[MAX_NUMNODES]; + unsigned int free_huge_pages_node[MAX_NUMNODES]; + unsigned int surplus_huge_pages_node[MAX_NUMNODES]; +}; + +extern struct hstate default_hstate; + +static inline struct hstate *hstate_vma(struct vm_area_struct *vma) +{ + return &default_hstate; +} + +static inline struct hstate *hstate_file(struct file *f) +{ + return &default_hstate; +} + +static inline struct hstate *hstate_inode(struct inode *i) +{ + return &default_hstate; +} + +static inline unsigned long huge_page_size(struct hstate *h) +{ + return (unsigned long)PAGE_SIZE << h->order; +} + +static inline unsigned long huge_page_mask(struct hstate *h) +{ + return h->mask; +} + +static inline unsigned int huge_page_order(struct hstate *h) +{ + return h->order; +} + +static inline unsigned huge_page_shift(struct hstate *h) +{ + return h->order + PAGE_SHIFT; +} + +static inline unsigned int pages_per_huge_page(struct hstate *h) +{ + return 1 << h->order; +} + +static inline unsigned int blocks_per_huge_page(struct hstate *h) +{ + return huge_page_size(h) / 512; +} + +#include + +#else +struct hstate {}; +#define hstate_file(f) NULL +#define hstate_vma(v) NULL +#define hstate_inode(i) NULL +#define huge_page_size(h) PAGE_SIZE +#define huge_page_mask(h) PAGE_MASK +#define huge_page_order(h) 0 +#define huge_page_shift(h) PAGE_SHIFT +#define pages_per_huge_page(h) 1 +#endif + #endif /* _LINUX_HUGETLB_H */ diff --git a/ipc/shm.c b/ipc/shm.c index 790240cd067f..a726aebce7d7 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -577,7 +577,8 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, if (is_file_hugepages(shp->shm_file)) { struct address_space *mapping = inode->i_mapping; - *rss += (HPAGE_SIZE/PAGE_SIZE)*mapping->nrpages; + struct hstate *h = hstate_file(shp->shm_file); + *rss += pages_per_huge_page(h) * mapping->nrpages; } else { struct shmem_inode_info *info = SHMEM_I(inode); spin_lock(&info->lock); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 32dff4290c66..0d8153e25f09 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -22,18 +22,12 @@ #include "internal.h" const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL; -static unsigned long nr_huge_pages, free_huge_pages, resv_huge_pages; -static unsigned long surplus_huge_pages; -static unsigned long nr_overcommit_huge_pages; unsigned long max_huge_pages; unsigned long sysctl_overcommit_huge_pages; -static struct list_head hugepage_freelists[MAX_NUMNODES]; -static unsigned int nr_huge_pages_node[MAX_NUMNODES]; -static unsigned int free_huge_pages_node[MAX_NUMNODES]; -static unsigned int surplus_huge_pages_node[MAX_NUMNODES]; static gfp_t htlb_alloc_mask = GFP_HIGHUSER; unsigned long hugepages_treat_as_movable; -static int hugetlb_next_nid; + +struct hstate default_hstate; /* * Protects updates to hugepage_freelists, nr_huge_pages, and free_huge_pages @@ -203,11 +197,11 @@ static long region_count(struct list_head *head, long f, long t) * Convert the address within this vma to the page offset within * the mapping, in pagecache page units; huge pages here. */ -static pgoff_t vma_hugecache_offset(struct vm_area_struct *vma, - unsigned long address) +static pgoff_t vma_hugecache_offset(struct hstate *h, + struct vm_area_struct *vma, unsigned long address) { - return ((address - vma->vm_start) >> HPAGE_SHIFT) + - (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT)); + return ((address - vma->vm_start) >> huge_page_shift(h)) + + (vma->vm_pgoff >> huge_page_order(h)); } /* @@ -309,20 +303,21 @@ static int is_vma_resv_set(struct vm_area_struct *vma, unsigned long flag) } /* Decrement the reserved pages in the hugepage pool by one */ -static void decrement_hugepage_resv_vma(struct vm_area_struct *vma) +static void decrement_hugepage_resv_vma(struct hstate *h, + struct vm_area_struct *vma) { if (vma->vm_flags & VM_NORESERVE) return; if (vma->vm_flags & VM_SHARED) { /* Shared mappings always use reserves */ - resv_huge_pages--; + h->resv_huge_pages--; } else if (is_vma_resv_set(vma, HPAGE_RESV_OWNER)) { /* * Only the process that called mmap() has reserves for * private mappings. */ - resv_huge_pages--; + h->resv_huge_pages--; } } @@ -344,12 +339,13 @@ static int vma_has_private_reserves(struct vm_area_struct *vma) return 1; } -static void clear_huge_page(struct page *page, unsigned long addr) +static void clear_huge_page(struct page *page, + unsigned long addr, unsigned long sz) { int i; might_sleep(); - for (i = 0; i < (HPAGE_SIZE/PAGE_SIZE); i++) { + for (i = 0; i < sz/PAGE_SIZE; i++) { cond_resched(); clear_user_highpage(page + i, addr + i * PAGE_SIZE); } @@ -359,41 +355,43 @@ static void copy_huge_page(struct page *dst, struct page *src, unsigned long addr, struct vm_area_struct *vma) { int i; + struct hstate *h = hstate_vma(vma); might_sleep(); - for (i = 0; i < HPAGE_SIZE/PAGE_SIZE; i++) { + for (i = 0; i < pages_per_huge_page(h); i++) { cond_resched(); copy_user_highpage(dst + i, src + i, addr + i*PAGE_SIZE, vma); } } -static void enqueue_huge_page(struct page *page) +static void enqueue_huge_page(struct hstate *h, struct page *page) { int nid = page_to_nid(page); - list_add(&page->lru, &hugepage_freelists[nid]); - free_huge_pages++; - free_huge_pages_node[nid]++; + list_add(&page->lru, &h->hugepage_freelists[nid]); + h->free_huge_pages++; + h->free_huge_pages_node[nid]++; } -static struct page *dequeue_huge_page(void) +static struct page *dequeue_huge_page(struct hstate *h) { int nid; struct page *page = NULL; for (nid = 0; nid < MAX_NUMNODES; ++nid) { - if (!list_empty(&hugepage_freelists[nid])) { - page = list_entry(hugepage_freelists[nid].next, + if (!list_empty(&h->hugepage_freelists[nid])) { + page = list_entry(h->hugepage_freelists[nid].next, struct page, lru); list_del(&page->lru); - free_huge_pages--; - free_huge_pages_node[nid]--; + h->free_huge_pages--; + h->free_huge_pages_node[nid]--; break; } } return page; } -static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, +static struct page *dequeue_huge_page_vma(struct hstate *h, + struct vm_area_struct *vma, unsigned long address, int avoid_reserve) { int nid; @@ -411,26 +409,26 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, * not "stolen". The child may still get SIGKILLed */ if (!vma_has_private_reserves(vma) && - free_huge_pages - resv_huge_pages == 0) + h->free_huge_pages - h->resv_huge_pages == 0) return NULL; /* If reserves cannot be used, ensure enough pages are in the pool */ - if (avoid_reserve && free_huge_pages - resv_huge_pages == 0) + if (avoid_reserve && h->free_huge_pages - h->resv_huge_pages == 0) return NULL; for_each_zone_zonelist_nodemask(zone, z, zonelist, MAX_NR_ZONES - 1, nodemask) { nid = zone_to_nid(zone); if (cpuset_zone_allowed_softwall(zone, htlb_alloc_mask) && - !list_empty(&hugepage_freelists[nid])) { - page = list_entry(hugepage_freelists[nid].next, + !list_empty(&h->hugepage_freelists[nid])) { + page = list_entry(h->hugepage_freelists[nid].next, struct page, lru); list_del(&page->lru); - free_huge_pages--; - free_huge_pages_node[nid]--; + h->free_huge_pages--; + h->free_huge_pages_node[nid]--; if (!avoid_reserve) - decrement_hugepage_resv_vma(vma); + decrement_hugepage_resv_vma(h, vma); break; } @@ -439,12 +437,13 @@ static struct page *dequeue_huge_page_vma(struct vm_area_struct *vma, return page; } -static void update_and_free_page(struct page *page) +static void update_and_free_page(struct hstate *h, struct page *page) { int i; - nr_huge_pages--; - nr_huge_pages_node[page_to_nid(page)]--; - for (i = 0; i < (HPAGE_SIZE / PAGE_SIZE); i++) { + + h->nr_huge_pages--; + h->nr_huge_pages_node[page_to_nid(page)]--; + for (i = 0; i < pages_per_huge_page(h); i++) { page[i].flags &= ~(1 << PG_locked | 1 << PG_error | 1 << PG_referenced | 1 << PG_dirty | 1 << PG_active | 1 << PG_reserved | 1 << PG_private | 1<< PG_writeback); @@ -452,11 +451,16 @@ static void update_and_free_page(struct page *page) set_compound_page_dtor(page, NULL); set_page_refcounted(page); arch_release_hugepage(page); - __free_pages(page, HUGETLB_PAGE_ORDER); + __free_pages(page, huge_page_order(h)); } static void free_huge_page(struct page *page) { + /* + * Can't pass hstate in here because it is called from the + * compound page destructor. + */ + struct hstate *h = &default_hstate; int nid = page_to_nid(page); struct address_space *mapping; @@ -466,12 +470,12 @@ static void free_huge_page(struct page *page) INIT_LIST_HEAD(&page->lru); spin_lock(&hugetlb_lock); - if (surplus_huge_pages_node[nid]) { - update_and_free_page(page); - surplus_huge_pages--; - surplus_huge_pages_node[nid]--; + if (h->surplus_huge_pages_node[nid]) { + update_and_free_page(h, page); + h->surplus_huge_pages--; + h->surplus_huge_pages_node[nid]--; } else { - enqueue_huge_page(page); + enqueue_huge_page(h, page); } spin_unlock(&hugetlb_lock); if (mapping) @@ -483,7 +487,7 @@ static void free_huge_page(struct page *page) * balanced by operating on them in a round-robin fashion. * Returns 1 if an adjustment was made. */ -static int adjust_pool_surplus(int delta) +static int adjust_pool_surplus(struct hstate *h, int delta) { static int prev_nid; int nid = prev_nid; @@ -496,15 +500,15 @@ static int adjust_pool_surplus(int delta) nid = first_node(node_online_map); /* To shrink on this node, there must be a surplus page */ - if (delta < 0 && !surplus_huge_pages_node[nid]) + if (delta < 0 && !h->surplus_huge_pages_node[nid]) continue; /* Surplus cannot exceed the total number of pages */ - if (delta > 0 && surplus_huge_pages_node[nid] >= - nr_huge_pages_node[nid]) + if (delta > 0 && h->surplus_huge_pages_node[nid] >= + h->nr_huge_pages_node[nid]) continue; - surplus_huge_pages += delta; - surplus_huge_pages_node[nid] += delta; + h->surplus_huge_pages += delta; + h->surplus_huge_pages_node[nid] += delta; ret = 1; break; } while (nid != prev_nid); @@ -513,46 +517,46 @@ static int adjust_pool_surplus(int delta) return ret; } -static void prep_new_huge_page(struct page *page, int nid) +static void prep_new_huge_page(struct hstate *h, struct page *page, int nid) { set_compound_page_dtor(page, free_huge_page); spin_lock(&hugetlb_lock); - nr_huge_pages++; - nr_huge_pages_node[nid]++; + h->nr_huge_pages++; + h->nr_huge_pages_node[nid]++; spin_unlock(&hugetlb_lock); put_page(page); /* free it into the hugepage allocator */ } -static struct page *alloc_fresh_huge_page_node(int nid) +static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid) { struct page *page; page = alloc_pages_node(nid, htlb_alloc_mask|__GFP_COMP|__GFP_THISNODE| __GFP_REPEAT|__GFP_NOWARN, - HUGETLB_PAGE_ORDER); + huge_page_order(h)); if (page) { if (arch_prepare_hugepage(page)) { __free_pages(page, HUGETLB_PAGE_ORDER); return NULL; } - prep_new_huge_page(page, nid); + prep_new_huge_page(h, page, nid); } return page; } -static int alloc_fresh_huge_page(void) +static int alloc_fresh_huge_page(struct hstate *h) { struct page *page; int start_nid; int next_nid; int ret = 0; - start_nid = hugetlb_next_nid; + start_nid = h->hugetlb_next_nid; do { - page = alloc_fresh_huge_page_node(hugetlb_next_nid); + page = alloc_fresh_huge_page_node(h, h->hugetlb_next_nid); if (page) ret = 1; /* @@ -566,11 +570,11 @@ static int alloc_fresh_huge_page(void) * if we just successfully allocated a hugepage so that * the next caller gets hugepages on the next node. */ - next_nid = next_node(hugetlb_next_nid, node_online_map); + next_nid = next_node(h->hugetlb_next_nid, node_online_map); if (next_nid == MAX_NUMNODES) next_nid = first_node(node_online_map); - hugetlb_next_nid = next_nid; - } while (!page && hugetlb_next_nid != start_nid); + h->hugetlb_next_nid = next_nid; + } while (!page && h->hugetlb_next_nid != start_nid); if (ret) count_vm_event(HTLB_BUDDY_PGALLOC); @@ -580,8 +584,8 @@ static int alloc_fresh_huge_page(void) return ret; } -static struct page *alloc_buddy_huge_page(struct vm_area_struct *vma, - unsigned long address) +static struct page *alloc_buddy_huge_page(struct hstate *h, + struct vm_area_struct *vma, unsigned long address) { struct page *page; unsigned int nid; @@ -610,18 +614,18 @@ static struct page *alloc_buddy_huge_page(struct vm_area_struct *vma, * per-node value is checked there. */ spin_lock(&hugetlb_lock); - if (surplus_huge_pages >= nr_overcommit_huge_pages) { + if (h->surplus_huge_pages >= h->nr_overcommit_huge_pages) { spin_unlock(&hugetlb_lock); return NULL; } else { - nr_huge_pages++; - surplus_huge_pages++; + h->nr_huge_pages++; + h->surplus_huge_pages++; } spin_unlock(&hugetlb_lock); page = alloc_pages(htlb_alloc_mask|__GFP_COMP| __GFP_REPEAT|__GFP_NOWARN, - HUGETLB_PAGE_ORDER); + huge_page_order(h)); spin_lock(&hugetlb_lock); if (page) { @@ -636,12 +640,12 @@ static struct page *alloc_buddy_huge_page(struct vm_area_struct *vma, /* * We incremented the global counters already */ - nr_huge_pages_node[nid]++; - surplus_huge_pages_node[nid]++; + h->nr_huge_pages_node[nid]++; + h->surplus_huge_pages_node[nid]++; __count_vm_event(HTLB_BUDDY_PGALLOC); } else { - nr_huge_pages--; - surplus_huge_pages--; + h->nr_huge_pages--; + h->surplus_huge_pages--; __count_vm_event(HTLB_BUDDY_PGALLOC_FAIL); } spin_unlock(&hugetlb_lock); @@ -653,16 +657,16 @@ static struct page *alloc_buddy_huge_page(struct vm_area_struct *vma, * Increase the hugetlb pool such that it can accomodate a reservation * of size 'delta'. */ -static int gather_surplus_pages(int delta) +static int gather_surplus_pages(struct hstate *h, int delta) { struct list_head surplus_list; struct page *page, *tmp; int ret, i; int needed, allocated; - needed = (resv_huge_pages + delta) - free_huge_pages; + needed = (h->resv_huge_pages + delta) - h->free_huge_pages; if (needed <= 0) { - resv_huge_pages += delta; + h->resv_huge_pages += delta; return 0; } @@ -673,7 +677,7 @@ static int gather_surplus_pages(int delta) retry: spin_unlock(&hugetlb_lock); for (i = 0; i < needed; i++) { - page = alloc_buddy_huge_page(NULL, 0); + page = alloc_buddy_huge_page(h, NULL, 0); if (!page) { /* * We were not able to allocate enough pages to @@ -694,7 +698,8 @@ retry: * because either resv_huge_pages or free_huge_pages may have changed. */ spin_lock(&hugetlb_lock); - needed = (resv_huge_pages + delta) - (free_huge_pages + allocated); + needed = (h->resv_huge_pages + delta) - + (h->free_huge_pages + allocated); if (needed > 0) goto retry; @@ -707,7 +712,7 @@ retry: * before they are reserved. */ needed += allocated; - resv_huge_pages += delta; + h->resv_huge_pages += delta; ret = 0; free: /* Free the needed pages to the hugetlb pool */ @@ -715,7 +720,7 @@ free: if ((--needed) < 0) break; list_del(&page->lru); - enqueue_huge_page(page); + enqueue_huge_page(h, page); } /* Free unnecessary surplus pages to the buddy allocator */ @@ -743,7 +748,8 @@ free: * allocated to satisfy the reservation must be explicitly freed if they were * never used. */ -static void return_unused_surplus_pages(unsigned long unused_resv_pages) +static void return_unused_surplus_pages(struct hstate *h, + unsigned long unused_resv_pages) { static int nid = -1; struct page *page; @@ -758,27 +764,27 @@ static void return_unused_surplus_pages(unsigned long unused_resv_pages) unsigned long remaining_iterations = num_online_nodes(); /* Uncommit the reservation */ - resv_huge_pages -= unused_resv_pages; + h->resv_huge_pages -= unused_resv_pages; - nr_pages = min(unused_resv_pages, surplus_huge_pages); + nr_pages = min(unused_resv_pages, h->surplus_huge_pages); while (remaining_iterations-- && nr_pages) { nid = next_node(nid, node_online_map); if (nid == MAX_NUMNODES) nid = first_node(node_online_map); - if (!surplus_huge_pages_node[nid]) + if (!h->surplus_huge_pages_node[nid]) continue; - if (!list_empty(&hugepage_freelists[nid])) { - page = list_entry(hugepage_freelists[nid].next, + if (!list_empty(&h->hugepage_freelists[nid])) { + page = list_entry(h->hugepage_freelists[nid].next, struct page, lru); list_del(&page->lru); - update_and_free_page(page); - free_huge_pages--; - free_huge_pages_node[nid]--; - surplus_huge_pages--; - surplus_huge_pages_node[nid]--; + update_and_free_page(h, page); + h->free_huge_pages--; + h->free_huge_pages_node[nid]--; + h->surplus_huge_pages--; + h->surplus_huge_pages_node[nid]--; nr_pages--; remaining_iterations = num_online_nodes(); } @@ -794,13 +800,14 @@ static void return_unused_surplus_pages(unsigned long unused_resv_pages) * an instantiated the change should be committed via vma_commit_reservation. * No action is required on failure. */ -static int vma_needs_reservation(struct vm_area_struct *vma, unsigned long addr) +static int vma_needs_reservation(struct hstate *h, + struct vm_area_struct *vma, unsigned long addr) { struct address_space *mapping = vma->vm_file->f_mapping; struct inode *inode = mapping->host; if (vma->vm_flags & VM_SHARED) { - pgoff_t idx = vma_hugecache_offset(vma, addr); + pgoff_t idx = vma_hugecache_offset(h, vma, addr); return region_chg(&inode->i_mapping->private_list, idx, idx + 1); @@ -809,7 +816,7 @@ static int vma_needs_reservation(struct vm_area_struct *vma, unsigned long addr) } else { int err; - pgoff_t idx = vma_hugecache_offset(vma, addr); + pgoff_t idx = vma_hugecache_offset(h, vma, addr); struct resv_map *reservations = vma_resv_map(vma); err = region_chg(&reservations->regions, idx, idx + 1); @@ -818,18 +825,18 @@ static int vma_needs_reservation(struct vm_area_struct *vma, unsigned long addr) return 0; } } -static void vma_commit_reservation(struct vm_area_struct *vma, - unsigned long addr) +static void vma_commit_reservation(struct hstate *h, + struct vm_area_struct *vma, unsigned long addr) { struct address_space *mapping = vma->vm_file->f_mapping; struct inode *inode = mapping->host; if (vma->vm_flags & VM_SHARED) { - pgoff_t idx = vma_hugecache_offset(vma, addr); + pgoff_t idx = vma_hugecache_offset(h, vma, addr); region_add(&inode->i_mapping->private_list, idx, idx + 1); } else if (is_vma_resv_set(vma, HPAGE_RESV_OWNER)) { - pgoff_t idx = vma_hugecache_offset(vma, addr); + pgoff_t idx = vma_hugecache_offset(h, vma, addr); struct resv_map *reservations = vma_resv_map(vma); /* Mark this page used in the map. */ @@ -840,6 +847,7 @@ static void vma_commit_reservation(struct vm_area_struct *vma, static struct page *alloc_huge_page(struct vm_area_struct *vma, unsigned long addr, int avoid_reserve) { + struct hstate *h = hstate_vma(vma); struct page *page; struct address_space *mapping = vma->vm_file->f_mapping; struct inode *inode = mapping->host; @@ -852,7 +860,7 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, * MAP_NORESERVE mappings may also need pages and quota allocated * if no reserve mapping overlaps. */ - chg = vma_needs_reservation(vma, addr); + chg = vma_needs_reservation(h, vma, addr); if (chg < 0) return ERR_PTR(chg); if (chg) @@ -860,11 +868,11 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, return ERR_PTR(-ENOSPC); spin_lock(&hugetlb_lock); - page = dequeue_huge_page_vma(vma, addr, avoid_reserve); + page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve); spin_unlock(&hugetlb_lock); if (!page) { - page = alloc_buddy_huge_page(vma, addr); + page = alloc_buddy_huge_page(h, vma, addr); if (!page) { hugetlb_put_quota(inode->i_mapping, chg); return ERR_PTR(-VM_FAULT_OOM); @@ -874,7 +882,7 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, set_page_refcounted(page); set_page_private(page, (unsigned long) mapping); - vma_commit_reservation(vma, addr); + vma_commit_reservation(h, vma, addr); return page; } @@ -882,21 +890,28 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, static int __init hugetlb_init(void) { unsigned long i; + struct hstate *h = &default_hstate; if (HPAGE_SHIFT == 0) return 0; + if (!h->order) { + h->order = HPAGE_SHIFT - PAGE_SHIFT; + h->mask = HPAGE_MASK; + } + for (i = 0; i < MAX_NUMNODES; ++i) - INIT_LIST_HEAD(&hugepage_freelists[i]); + INIT_LIST_HEAD(&h->hugepage_freelists[i]); - hugetlb_next_nid = first_node(node_online_map); + h->hugetlb_next_nid = first_node(node_online_map); for (i = 0; i < max_huge_pages; ++i) { - if (!alloc_fresh_huge_page()) + if (!alloc_fresh_huge_page(h)) break; } - max_huge_pages = free_huge_pages = nr_huge_pages = i; - printk("Total HugeTLB memory allocated, %ld\n", free_huge_pages); + max_huge_pages = h->free_huge_pages = h->nr_huge_pages = i; + printk(KERN_INFO "Total HugeTLB memory allocated, %ld\n", + h->free_huge_pages); return 0; } module_init(hugetlb_init); @@ -922,34 +937,36 @@ static unsigned int cpuset_mems_nr(unsigned int *array) #ifdef CONFIG_SYSCTL #ifdef CONFIG_HIGHMEM -static void try_to_free_low(unsigned long count) +static void try_to_free_low(struct hstate *h, unsigned long count) { int i; for (i = 0; i < MAX_NUMNODES; ++i) { struct page *page, *next; - list_for_each_entry_safe(page, next, &hugepage_freelists[i], lru) { - if (count >= nr_huge_pages) + struct list_head *freel = &h->hugepage_freelists[i]; + list_for_each_entry_safe(page, next, freel, lru) { + if (count >= h->nr_huge_pages) return; if (PageHighMem(page)) continue; list_del(&page->lru); update_and_free_page(page); - free_huge_pages--; - free_huge_pages_node[page_to_nid(page)]--; + h->free_huge_pages--; + h->free_huge_pages_node[page_to_nid(page)]--; } } } #else -static inline void try_to_free_low(unsigned long count) +static inline void try_to_free_low(struct hstate *h, unsigned long count) { } #endif -#define persistent_huge_pages (nr_huge_pages - surplus_huge_pages) +#define persistent_huge_pages(h) (h->nr_huge_pages - h->surplus_huge_pages) static unsigned long set_max_huge_pages(unsigned long count) { unsigned long min_count, ret; + struct hstate *h = &default_hstate; /* * Increase the pool size @@ -963,19 +980,19 @@ static unsigned long set_max_huge_pages(unsigned long count) * within all the constraints specified by the sysctls. */ spin_lock(&hugetlb_lock); - while (surplus_huge_pages && count > persistent_huge_pages) { - if (!adjust_pool_surplus(-1)) + while (h->surplus_huge_pages && count > persistent_huge_pages(h)) { + if (!adjust_pool_surplus(h, -1)) break; } - while (count > persistent_huge_pages) { + while (count > persistent_huge_pages(h)) { /* * If this allocation races such that we no longer need the * page, free_huge_page will handle it by freeing the page * and reducing the surplus. */ spin_unlock(&hugetlb_lock); - ret = alloc_fresh_huge_page(); + ret = alloc_fresh_huge_page(h); spin_lock(&hugetlb_lock); if (!ret) goto out; @@ -997,21 +1014,21 @@ static unsigned long set_max_huge_pages(unsigned long count) * and won't grow the pool anywhere else. Not until one of the * sysctls are changed, or the surplus pages go out of use. */ - min_count = resv_huge_pages + nr_huge_pages - free_huge_pages; + min_count = h->resv_huge_pages + h->nr_huge_pages - h->free_huge_pages; min_count = max(count, min_count); - try_to_free_low(min_count); - while (min_count < persistent_huge_pages) { - struct page *page = dequeue_huge_page(); + try_to_free_low(h, min_count); + while (min_count < persistent_huge_pages(h)) { + struct page *page = dequeue_huge_page(h); if (!page) break; - update_and_free_page(page); + update_and_free_page(h, page); } - while (count < persistent_huge_pages) { - if (!adjust_pool_surplus(1)) + while (count < persistent_huge_pages(h)) { + if (!adjust_pool_surplus(h, 1)) break; } out: - ret = persistent_huge_pages; + ret = persistent_huge_pages(h); spin_unlock(&hugetlb_lock); return ret; } @@ -1041,9 +1058,10 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length, loff_t *ppos) { + struct hstate *h = &default_hstate; proc_doulongvec_minmax(table, write, file, buffer, length, ppos); spin_lock(&hugetlb_lock); - nr_overcommit_huge_pages = sysctl_overcommit_huge_pages; + h->nr_overcommit_huge_pages = sysctl_overcommit_huge_pages; spin_unlock(&hugetlb_lock); return 0; } @@ -1052,37 +1070,40 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write, int hugetlb_report_meminfo(char *buf) { + struct hstate *h = &default_hstate; return sprintf(buf, "HugePages_Total: %5lu\n" "HugePages_Free: %5lu\n" "HugePages_Rsvd: %5lu\n" "HugePages_Surp: %5lu\n" "Hugepagesize: %5lu kB\n", - nr_huge_pages, - free_huge_pages, - resv_huge_pages, - surplus_huge_pages, - HPAGE_SIZE/1024); + h->nr_huge_pages, + h->free_huge_pages, + h->resv_huge_pages, + h->surplus_huge_pages, + 1UL << (huge_page_order(h) + PAGE_SHIFT - 10)); } int hugetlb_report_node_meminfo(int nid, char *buf) { + struct hstate *h = &default_hstate; return sprintf(buf, "Node %d HugePages_Total: %5u\n" "Node %d HugePages_Free: %5u\n" "Node %d HugePages_Surp: %5u\n", - nid, nr_huge_pages_node[nid], - nid, free_huge_pages_node[nid], - nid, surplus_huge_pages_node[nid]); + nid, h->nr_huge_pages_node[nid], + nid, h->free_huge_pages_node[nid], + nid, h->surplus_huge_pages_node[nid]); } /* Return the number pages of memory we physically have, in PAGE_SIZE units. */ unsigned long hugetlb_total_pages(void) { - return nr_huge_pages * (HPAGE_SIZE / PAGE_SIZE); + struct hstate *h = &default_hstate; + return h->nr_huge_pages * pages_per_huge_page(h); } -static int hugetlb_acct_memory(long delta) +static int hugetlb_acct_memory(struct hstate *h, long delta) { int ret = -ENOMEM; @@ -1105,18 +1126,18 @@ static int hugetlb_acct_memory(long delta) * semantics that cpuset has. */ if (delta > 0) { - if (gather_surplus_pages(delta) < 0) + if (gather_surplus_pages(h, delta) < 0) goto out; - if (delta > cpuset_mems_nr(free_huge_pages_node)) { - return_unused_surplus_pages(delta); + if (delta > cpuset_mems_nr(h->free_huge_pages_node)) { + return_unused_surplus_pages(h, delta); goto out; } } ret = 0; if (delta < 0) - return_unused_surplus_pages((unsigned long) -delta); + return_unused_surplus_pages(h, (unsigned long) -delta); out: spin_unlock(&hugetlb_lock); @@ -1141,14 +1162,15 @@ static void hugetlb_vm_op_open(struct vm_area_struct *vma) static void hugetlb_vm_op_close(struct vm_area_struct *vma) { + struct hstate *h = hstate_vma(vma); struct resv_map *reservations = vma_resv_map(vma); unsigned long reserve; unsigned long start; unsigned long end; if (reservations) { - start = vma_hugecache_offset(vma, vma->vm_start); - end = vma_hugecache_offset(vma, vma->vm_end); + start = vma_hugecache_offset(h, vma, vma->vm_start); + end = vma_hugecache_offset(h, vma, vma->vm_end); reserve = (end - start) - region_count(&reservations->regions, start, end); @@ -1156,7 +1178,7 @@ static void hugetlb_vm_op_close(struct vm_area_struct *vma) kref_put(&reservations->refs, resv_map_release); if (reserve) - hugetlb_acct_memory(-reserve); + hugetlb_acct_memory(h, -reserve); } } @@ -1214,14 +1236,16 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, struct page *ptepage; unsigned long addr; int cow; + struct hstate *h = hstate_vma(vma); + unsigned long sz = huge_page_size(h); cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE; - for (addr = vma->vm_start; addr < vma->vm_end; addr += HPAGE_SIZE) { + for (addr = vma->vm_start; addr < vma->vm_end; addr += sz) { src_pte = huge_pte_offset(src, addr); if (!src_pte) continue; - dst_pte = huge_pte_alloc(dst, addr); + dst_pte = huge_pte_alloc(dst, addr, sz); if (!dst_pte) goto nomem; @@ -1257,6 +1281,9 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, pte_t pte; struct page *page; struct page *tmp; + struct hstate *h = hstate_vma(vma); + unsigned long sz = huge_page_size(h); + /* * A page gathering list, protected by per file i_mmap_lock. The * lock is used to avoid list corruption from multiple unmapping @@ -1265,11 +1292,11 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, LIST_HEAD(page_list); WARN_ON(!is_vm_hugetlb_page(vma)); - BUG_ON(start & ~HPAGE_MASK); - BUG_ON(end & ~HPAGE_MASK); + BUG_ON(start & ~huge_page_mask(h)); + BUG_ON(end & ~huge_page_mask(h)); spin_lock(&mm->page_table_lock); - for (address = start; address < end; address += HPAGE_SIZE) { + for (address = start; address < end; address += sz) { ptep = huge_pte_offset(mm, address); if (!ptep) continue; @@ -1383,6 +1410,7 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, pte_t *ptep, pte_t pte, struct page *pagecache_page) { + struct hstate *h = hstate_vma(vma); struct page *old_page, *new_page; int avoidcopy; int outside_reserve = 0; @@ -1443,7 +1471,7 @@ retry_avoidcopy: __SetPageUptodate(new_page); spin_lock(&mm->page_table_lock); - ptep = huge_pte_offset(mm, address & HPAGE_MASK); + ptep = huge_pte_offset(mm, address & huge_page_mask(h)); if (likely(pte_same(huge_ptep_get(ptep), pte))) { /* Break COW */ huge_ptep_clear_flush(vma, address, ptep); @@ -1458,14 +1486,14 @@ retry_avoidcopy: } /* Return the pagecache page at a given address within a VMA */ -static struct page *hugetlbfs_pagecache_page(struct vm_area_struct *vma, - unsigned long address) +static struct page *hugetlbfs_pagecache_page(struct hstate *h, + struct vm_area_struct *vma, unsigned long address) { struct address_space *mapping; pgoff_t idx; mapping = vma->vm_file->f_mapping; - idx = vma_hugecache_offset(vma, address); + idx = vma_hugecache_offset(h, vma, address); return find_lock_page(mapping, idx); } @@ -1473,6 +1501,7 @@ static struct page *hugetlbfs_pagecache_page(struct vm_area_struct *vma, static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, pte_t *ptep, int write_access) { + struct hstate *h = hstate_vma(vma); int ret = VM_FAULT_SIGBUS; pgoff_t idx; unsigned long size; @@ -1493,7 +1522,7 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, } mapping = vma->vm_file->f_mapping; - idx = vma_hugecache_offset(vma, address); + idx = vma_hugecache_offset(h, vma, address); /* * Use page lock to guard against racing truncation @@ -1502,7 +1531,7 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma, retry: page = find_lock_page(mapping, idx); if (!page) { - size = i_size_read(mapping->host) >> HPAGE_SHIFT; + size = i_size_read(mapping->host) >> huge_page_shift(h); if (idx >= size) goto out; page = alloc_huge_page(vma, address, 0); @@ -1510,7 +1539,7 @@ retry: ret = -PTR_ERR(page); goto out; } - clear_huge_page(page, address); + clear_huge_page(page, address, huge_page_size(h)); __SetPageUptodate(page); if (vma->vm_flags & VM_SHARED) { @@ -1526,14 +1555,14 @@ retry: } spin_lock(&inode->i_lock); - inode->i_blocks += BLOCKS_PER_HUGEPAGE; + inode->i_blocks += blocks_per_huge_page(h); spin_unlock(&inode->i_lock); } else lock_page(page); } spin_lock(&mm->page_table_lock); - size = i_size_read(mapping->host) >> HPAGE_SHIFT; + size = i_size_read(mapping->host) >> huge_page_shift(h); if (idx >= size) goto backout; @@ -1569,8 +1598,9 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, pte_t entry; int ret; static DEFINE_MUTEX(hugetlb_instantiation_mutex); + struct hstate *h = hstate_vma(vma); - ptep = huge_pte_alloc(mm, address); + ptep = huge_pte_alloc(mm, address, huge_page_size(h)); if (!ptep) return VM_FAULT_OOM; @@ -1594,7 +1624,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, if (likely(pte_same(entry, huge_ptep_get(ptep)))) if (write_access && !pte_write(entry)) { struct page *page; - page = hugetlbfs_pagecache_page(vma, address); + page = hugetlbfs_pagecache_page(h, vma, address); ret = hugetlb_cow(mm, vma, address, ptep, entry, page); if (page) { unlock_page(page); @@ -1615,6 +1645,7 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long pfn_offset; unsigned long vaddr = *position; int remainder = *length; + struct hstate *h = hstate_vma(vma); spin_lock(&mm->page_table_lock); while (vaddr < vma->vm_end && remainder) { @@ -1626,7 +1657,7 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, * each hugepage. We have to make * sure we get the * first, for the page indexing below to work. */ - pte = huge_pte_offset(mm, vaddr & HPAGE_MASK); + pte = huge_pte_offset(mm, vaddr & huge_page_mask(h)); if (!pte || huge_pte_none(huge_ptep_get(pte)) || (write && !pte_write(huge_ptep_get(pte)))) { @@ -1644,7 +1675,7 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, break; } - pfn_offset = (vaddr & ~HPAGE_MASK) >> PAGE_SHIFT; + pfn_offset = (vaddr & ~huge_page_mask(h)) >> PAGE_SHIFT; page = pte_page(huge_ptep_get(pte)); same_page: if (pages) { @@ -1660,7 +1691,7 @@ same_page: --remainder; ++i; if (vaddr < vma->vm_end && remainder && - pfn_offset < HPAGE_SIZE/PAGE_SIZE) { + pfn_offset < pages_per_huge_page(h)) { /* * We use pfn_offset to avoid touching the pageframes * of this compound page. @@ -1682,13 +1713,14 @@ void hugetlb_change_protection(struct vm_area_struct *vma, unsigned long start = address; pte_t *ptep; pte_t pte; + struct hstate *h = hstate_vma(vma); BUG_ON(address >= end); flush_cache_range(vma, address, end); spin_lock(&vma->vm_file->f_mapping->i_mmap_lock); spin_lock(&mm->page_table_lock); - for (; address < end; address += HPAGE_SIZE) { + for (; address < end; address += huge_page_size(h)) { ptep = huge_pte_offset(mm, address); if (!ptep) continue; @@ -1711,6 +1743,7 @@ int hugetlb_reserve_pages(struct inode *inode, struct vm_area_struct *vma) { long ret, chg; + struct hstate *h = hstate_inode(inode); if (vma && vma->vm_flags & VM_NORESERVE) return 0; @@ -1739,7 +1772,7 @@ int hugetlb_reserve_pages(struct inode *inode, if (hugetlb_get_quota(inode->i_mapping, chg)) return -ENOSPC; - ret = hugetlb_acct_memory(chg); + ret = hugetlb_acct_memory(h, chg); if (ret < 0) { hugetlb_put_quota(inode->i_mapping, chg); return ret; @@ -1751,12 +1784,13 @@ int hugetlb_reserve_pages(struct inode *inode, void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed) { + struct hstate *h = hstate_inode(inode); long chg = region_truncate(&inode->i_mapping->private_list, offset); spin_lock(&inode->i_lock); - inode->i_blocks -= BLOCKS_PER_HUGEPAGE * freed; + inode->i_blocks -= blocks_per_huge_page(h); spin_unlock(&inode->i_lock); hugetlb_put_quota(inode->i_mapping, (chg - freed)); - hugetlb_acct_memory(-(chg - freed)); + hugetlb_acct_memory(h, -(chg - freed)); } diff --git a/mm/memory.c b/mm/memory.c index 72932489a082..c1c1d6d8c22b 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -903,7 +903,7 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, if (unlikely(is_vm_hugetlb_page(vma))) { unmap_hugepage_range(vma, start, end, NULL); zap_work -= (end - start) / - (HPAGE_SIZE / PAGE_SIZE); + pages_per_huge_page(hstate_vma(vma)); start = end; } else start = unmap_page_range(*tlbp, vma, diff --git a/mm/mempolicy.c b/mm/mempolicy.c index c94e58b192c3..e550bec20582 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1481,7 +1481,7 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr, if (unlikely((*mpol)->mode == MPOL_INTERLEAVE)) { zl = node_zonelist(interleave_nid(*mpol, vma, addr, - HPAGE_SHIFT), gfp_flags); + huge_page_shift(hstate_vma(vma))), gfp_flags); } else { zl = policy_zonelist(gfp_flags, *mpol); if ((*mpol)->mode == MPOL_BIND) @@ -2220,9 +2220,12 @@ static void check_huge_range(struct vm_area_struct *vma, { unsigned long addr; struct page *page; + struct hstate *h = hstate_vma(vma); + unsigned long sz = huge_page_size(h); - for (addr = start; addr < end; addr += HPAGE_SIZE) { - pte_t *ptep = huge_pte_offset(vma->vm_mm, addr & HPAGE_MASK); + for (addr = start; addr < end; addr += sz) { + pte_t *ptep = huge_pte_offset(vma->vm_mm, + addr & huge_page_mask(h)); pte_t pte; if (!ptep) diff --git a/mm/mmap.c b/mm/mmap.c index 57d3b6097deb..5e0cc99e9cd5 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1812,7 +1812,8 @@ int split_vma(struct mm_struct * mm, struct vm_area_struct * vma, struct mempolicy *pol; struct vm_area_struct *new; - if (is_vm_hugetlb_page(vma) && (addr & ~HPAGE_MASK)) + if (is_vm_hugetlb_page(vma) && (addr & + ~(huge_page_mask(hstate_vma(vma))))) return -EINVAL; if (mm->map_count >= sysctl_max_map_count) -- cgit From e5ff215941d59f8ae6bf58f6428dc5c26745a612 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 23 Jul 2008 21:27:42 -0700 Subject: hugetlb: multiple hstates for multiple page sizes Add basic support for more than one hstate in hugetlbfs. This is the key to supporting multiple hugetlbfs page sizes at once. - Rather than a single hstate, we now have an array, with an iterator - default_hstate continues to be the struct hstate which we use by default - Add functions for architectures to register new hstates [akpm@linux-foundation.org: coding-style fixes] Acked-by: Adam Litke Acked-by: Nishanth Aravamudan Signed-off-by: Andi Kleen Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 19 ++++++- kernel/sysctl.c | 8 ++- mm/hugetlb.c | 148 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 142 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index ad2271e11f9b..b75bdb4deba3 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -36,8 +36,6 @@ int hugetlb_reserve_pages(struct inode *inode, long from, long to, struct vm_area_struct *vma); void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed); -extern unsigned long max_huge_pages; -extern unsigned long sysctl_overcommit_huge_pages; extern unsigned long hugepages_treat_as_movable; extern const unsigned long hugetlb_zero, hugetlb_infinity; extern int sysctl_hugetlb_shm_group; @@ -181,7 +179,17 @@ struct hstate { unsigned int surplus_huge_pages_node[MAX_NUMNODES]; }; -extern struct hstate default_hstate; +void __init hugetlb_add_hstate(unsigned order); +struct hstate *size_to_hstate(unsigned long size); + +#ifndef HUGE_MAX_HSTATE +#define HUGE_MAX_HSTATE 1 +#endif + +extern struct hstate hstates[HUGE_MAX_HSTATE]; +extern unsigned int default_hstate_idx; + +#define default_hstate (hstates[default_hstate_idx]) static inline struct hstate *hstate_vma(struct vm_area_struct *vma) { @@ -230,6 +238,11 @@ static inline unsigned int blocks_per_huge_page(struct hstate *h) #include +static inline struct hstate *page_hstate(struct page *page) +{ + return size_to_hstate(PAGE_SIZE << compound_order(page)); +} + #else struct hstate {}; #define hstate_file(f) NULL diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 1f7b3b76a166..1a8299d1fe59 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -959,7 +959,7 @@ static struct ctl_table vm_table[] = { #ifdef CONFIG_HUGETLB_PAGE { .procname = "nr_hugepages", - .data = &max_huge_pages, + .data = NULL, .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = &hugetlb_sysctl_handler, @@ -985,10 +985,12 @@ static struct ctl_table vm_table[] = { { .ctl_name = CTL_UNNUMBERED, .procname = "nr_overcommit_hugepages", - .data = &sysctl_overcommit_huge_pages, - .maxlen = sizeof(sysctl_overcommit_huge_pages), + .data = NULL, + .maxlen = sizeof(unsigned long), .mode = 0644, .proc_handler = &hugetlb_overcommit_handler, + .extra1 = (void *)&hugetlb_zero, + .extra2 = (void *)&hugetlb_infinity, }, #endif { diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 0d8153e25f09..82378d44a0c5 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -22,12 +22,19 @@ #include "internal.h" const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL; -unsigned long max_huge_pages; -unsigned long sysctl_overcommit_huge_pages; static gfp_t htlb_alloc_mask = GFP_HIGHUSER; unsigned long hugepages_treat_as_movable; -struct hstate default_hstate; +static int max_hstate; +unsigned int default_hstate_idx; +struct hstate hstates[HUGE_MAX_HSTATE]; + +/* for command line parsing */ +static struct hstate * __initdata parsed_hstate; +static unsigned long __initdata default_hstate_max_huge_pages; + +#define for_each_hstate(h) \ + for ((h) = hstates; (h) < &hstates[max_hstate]; (h)++) /* * Protects updates to hugepage_freelists, nr_huge_pages, and free_huge_pages @@ -454,13 +461,24 @@ static void update_and_free_page(struct hstate *h, struct page *page) __free_pages(page, huge_page_order(h)); } +struct hstate *size_to_hstate(unsigned long size) +{ + struct hstate *h; + + for_each_hstate(h) { + if (huge_page_size(h) == size) + return h; + } + return NULL; +} + static void free_huge_page(struct page *page) { /* * Can't pass hstate in here because it is called from the * compound page destructor. */ - struct hstate *h = &default_hstate; + struct hstate *h = page_hstate(page); int nid = page_to_nid(page); struct address_space *mapping; @@ -887,39 +905,94 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, return page; } -static int __init hugetlb_init(void) +static void __init hugetlb_init_one_hstate(struct hstate *h) { unsigned long i; - struct hstate *h = &default_hstate; - - if (HPAGE_SHIFT == 0) - return 0; - - if (!h->order) { - h->order = HPAGE_SHIFT - PAGE_SHIFT; - h->mask = HPAGE_MASK; - } for (i = 0; i < MAX_NUMNODES; ++i) INIT_LIST_HEAD(&h->hugepage_freelists[i]); h->hugetlb_next_nid = first_node(node_online_map); - for (i = 0; i < max_huge_pages; ++i) { + for (i = 0; i < h->max_huge_pages; ++i) { if (!alloc_fresh_huge_page(h)) break; } - max_huge_pages = h->free_huge_pages = h->nr_huge_pages = i; - printk(KERN_INFO "Total HugeTLB memory allocated, %ld\n", - h->free_huge_pages); + h->max_huge_pages = h->free_huge_pages = h->nr_huge_pages = i; +} + +static void __init hugetlb_init_hstates(void) +{ + struct hstate *h; + + for_each_hstate(h) { + hugetlb_init_one_hstate(h); + } +} + +static void __init report_hugepages(void) +{ + struct hstate *h; + + for_each_hstate(h) { + printk(KERN_INFO "Total HugeTLB memory allocated, " + "%ld %dMB pages\n", + h->free_huge_pages, + 1 << (h->order + PAGE_SHIFT - 20)); + } +} + +static int __init hugetlb_init(void) +{ + BUILD_BUG_ON(HPAGE_SHIFT == 0); + + if (!size_to_hstate(HPAGE_SIZE)) { + hugetlb_add_hstate(HUGETLB_PAGE_ORDER); + parsed_hstate->max_huge_pages = default_hstate_max_huge_pages; + } + default_hstate_idx = size_to_hstate(HPAGE_SIZE) - hstates; + + hugetlb_init_hstates(); + + report_hugepages(); + return 0; } module_init(hugetlb_init); +/* Should be called on processing a hugepagesz=... option */ +void __init hugetlb_add_hstate(unsigned order) +{ + struct hstate *h; + if (size_to_hstate(PAGE_SIZE << order)) { + printk(KERN_WARNING "hugepagesz= specified twice, ignoring\n"); + return; + } + BUG_ON(max_hstate >= HUGE_MAX_HSTATE); + BUG_ON(order == 0); + h = &hstates[max_hstate++]; + h->order = order; + h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1); + hugetlb_init_one_hstate(h); + parsed_hstate = h; +} + static int __init hugetlb_setup(char *s) { - if (sscanf(s, "%lu", &max_huge_pages) <= 0) - max_huge_pages = 0; + unsigned long *mhp; + + /* + * !max_hstate means we haven't parsed a hugepagesz= parameter yet, + * so this hugepages= parameter goes to the "default hstate". + */ + if (!max_hstate) + mhp = &default_hstate_max_huge_pages; + else + mhp = &parsed_hstate->max_huge_pages; + + if (sscanf(s, "%lu", mhp) <= 0) + *mhp = 0; + return 1; } __setup("hugepages=", hugetlb_setup); @@ -950,7 +1023,7 @@ static void try_to_free_low(struct hstate *h, unsigned long count) if (PageHighMem(page)) continue; list_del(&page->lru); - update_and_free_page(page); + update_and_free_page(h, page); h->free_huge_pages--; h->free_huge_pages_node[page_to_nid(page)]--; } @@ -963,10 +1036,9 @@ static inline void try_to_free_low(struct hstate *h, unsigned long count) #endif #define persistent_huge_pages(h) (h->nr_huge_pages - h->surplus_huge_pages) -static unsigned long set_max_huge_pages(unsigned long count) +static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count) { unsigned long min_count, ret; - struct hstate *h = &default_hstate; /* * Increase the pool size @@ -1037,8 +1109,19 @@ int hugetlb_sysctl_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length, loff_t *ppos) { + struct hstate *h = &default_hstate; + unsigned long tmp; + + if (!write) + tmp = h->max_huge_pages; + + table->data = &tmp; + table->maxlen = sizeof(unsigned long); proc_doulongvec_minmax(table, write, file, buffer, length, ppos); - max_huge_pages = set_max_huge_pages(max_huge_pages); + + if (write) + h->max_huge_pages = set_max_huge_pages(h, tmp); + return 0; } @@ -1059,10 +1142,21 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write, size_t *length, loff_t *ppos) { struct hstate *h = &default_hstate; + unsigned long tmp; + + if (!write) + tmp = h->nr_overcommit_huge_pages; + + table->data = &tmp; + table->maxlen = sizeof(unsigned long); proc_doulongvec_minmax(table, write, file, buffer, length, ppos); - spin_lock(&hugetlb_lock); - h->nr_overcommit_huge_pages = sysctl_overcommit_huge_pages; - spin_unlock(&hugetlb_lock); + + if (write) { + spin_lock(&hugetlb_lock); + h->nr_overcommit_huge_pages = tmp; + spin_unlock(&hugetlb_lock); + } + return 0; } -- cgit From a137e1cc6d6e7d315fef03962a2a5a113348b13b Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 23 Jul 2008 21:27:43 -0700 Subject: hugetlbfs: per mount huge page sizes Add the ability to configure the hugetlb hstate used on a per mount basis. - Add a new pagesize= option to the hugetlbfs mount that allows setting the page size - This option causes the mount code to find the hstate corresponding to the specified size, and sets up a pointer to the hstate in the mount's superblock. - Change the hstate accessors to use this information rather than the global_hstate they were using (requires a slight change in mm/memory.c so we don't NULL deref in the error-unmap path -- see comments). [np: take hstate out of hugetlbfs inode and vma->vm_private_data] Acked-by: Adam Litke Acked-by: Nishanth Aravamudan Signed-off-by: Andi Kleen Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/hugetlbfs/inode.c | 45 ++++++++++++++++++++++++++++++++++++--------- include/linux/hugetlb.h | 14 +++++++++----- mm/hugetlb.c | 16 +++------------- mm/memory.c | 18 ++++++++++++++++-- 4 files changed, 64 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 516c581b5371..dbd01d262ca4 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -53,6 +53,7 @@ int sysctl_hugetlb_shm_group; enum { Opt_size, Opt_nr_inodes, Opt_mode, Opt_uid, Opt_gid, + Opt_pagesize, Opt_err, }; @@ -62,6 +63,7 @@ static match_table_t tokens = { {Opt_mode, "mode=%o"}, {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, + {Opt_pagesize, "pagesize=%s"}, {Opt_err, NULL}, }; @@ -750,6 +752,8 @@ hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) char *p, *rest; substring_t args[MAX_OPT_ARGS]; int option; + unsigned long long size = 0; + enum { NO_SIZE, SIZE_STD, SIZE_PERCENT } setsize = NO_SIZE; if (!options) return 0; @@ -780,17 +784,13 @@ hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) break; case Opt_size: { - unsigned long long size; /* memparse() will accept a K/M/G without a digit */ if (!isdigit(*args[0].from)) goto bad_val; size = memparse(args[0].from, &rest); - if (*rest == '%') { - size <<= HPAGE_SHIFT; - size *= max_huge_pages; - do_div(size, 100); - } - pconfig->nr_blocks = (size >> HPAGE_SHIFT); + setsize = SIZE_STD; + if (*rest == '%') + setsize = SIZE_PERCENT; break; } @@ -801,6 +801,19 @@ hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) pconfig->nr_inodes = memparse(args[0].from, &rest); break; + case Opt_pagesize: { + unsigned long ps; + ps = memparse(args[0].from, &rest); + pconfig->hstate = size_to_hstate(ps); + if (!pconfig->hstate) { + printk(KERN_ERR + "hugetlbfs: Unsupported page size %lu MB\n", + ps >> 20); + return -EINVAL; + } + break; + } + default: printk(KERN_ERR "hugetlbfs: Bad mount option: \"%s\"\n", p); @@ -808,6 +821,18 @@ hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) break; } } + + /* Do size after hstate is set up */ + if (setsize > NO_SIZE) { + struct hstate *h = pconfig->hstate; + if (setsize == SIZE_PERCENT) { + size <<= huge_page_shift(h); + size *= h->max_huge_pages; + do_div(size, 100); + } + pconfig->nr_blocks = (size >> huge_page_shift(h)); + } + return 0; bad_val: @@ -832,6 +857,7 @@ hugetlbfs_fill_super(struct super_block *sb, void *data, int silent) config.uid = current->fsuid; config.gid = current->fsgid; config.mode = 0755; + config.hstate = &default_hstate; ret = hugetlbfs_parse_options(data, &config); if (ret) return ret; @@ -840,14 +866,15 @@ hugetlbfs_fill_super(struct super_block *sb, void *data, int silent) if (!sbinfo) return -ENOMEM; sb->s_fs_info = sbinfo; + sbinfo->hstate = config.hstate; spin_lock_init(&sbinfo->stat_lock); sbinfo->max_blocks = config.nr_blocks; sbinfo->free_blocks = config.nr_blocks; sbinfo->max_inodes = config.nr_inodes; sbinfo->free_inodes = config.nr_inodes; sb->s_maxbytes = MAX_LFS_FILESIZE; - sb->s_blocksize = HPAGE_SIZE; - sb->s_blocksize_bits = HPAGE_SHIFT; + sb->s_blocksize = huge_page_size(config.hstate); + sb->s_blocksize_bits = huge_page_shift(config.hstate); sb->s_magic = HUGETLBFS_MAGIC; sb->s_op = &hugetlbfs_ops; sb->s_time_gran = 1; diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index b75bdb4deba3..ba9263e631b9 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -100,6 +100,7 @@ struct hugetlbfs_config { umode_t mode; long nr_blocks; long nr_inodes; + struct hstate *hstate; }; struct hugetlbfs_sb_info { @@ -108,6 +109,7 @@ struct hugetlbfs_sb_info { long max_inodes; /* inodes allowed */ long free_inodes; /* inodes free */ spinlock_t stat_lock; + struct hstate *hstate; }; @@ -191,19 +193,21 @@ extern unsigned int default_hstate_idx; #define default_hstate (hstates[default_hstate_idx]) -static inline struct hstate *hstate_vma(struct vm_area_struct *vma) +static inline struct hstate *hstate_inode(struct inode *i) { - return &default_hstate; + struct hugetlbfs_sb_info *hsb; + hsb = HUGETLBFS_SB(i->i_sb); + return hsb->hstate; } static inline struct hstate *hstate_file(struct file *f) { - return &default_hstate; + return hstate_inode(f->f_dentry->d_inode); } -static inline struct hstate *hstate_inode(struct inode *i) +static inline struct hstate *hstate_vma(struct vm_area_struct *vma) { - return &default_hstate; + return hstate_file(vma->vm_file); } static inline unsigned long huge_page_size(struct hstate *h) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 82378d44a0c5..4cf7a90e9140 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1439,19 +1439,9 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page) { - /* - * It is undesirable to test vma->vm_file as it should be non-null - * for valid hugetlb area. However, vm_file will be NULL in the error - * cleanup path of do_mmap_pgoff. When hugetlbfs ->mmap method fails, - * do_mmap_pgoff() nullifies vma->vm_file before calling this function - * to clean up. Since no pte has actually been setup, it is safe to - * do nothing in this case. - */ - if (vma->vm_file) { - spin_lock(&vma->vm_file->f_mapping->i_mmap_lock); - __unmap_hugepage_range(vma, start, end, ref_page); - spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock); - } + spin_lock(&vma->vm_file->f_mapping->i_mmap_lock); + __unmap_hugepage_range(vma, start, end, ref_page); + spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock); } /* diff --git a/mm/memory.c b/mm/memory.c index c1c1d6d8c22b..02fc6b1047b0 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -901,9 +901,23 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, } if (unlikely(is_vm_hugetlb_page(vma))) { - unmap_hugepage_range(vma, start, end, NULL); - zap_work -= (end - start) / + /* + * It is undesirable to test vma->vm_file as it + * should be non-null for valid hugetlb area. + * However, vm_file will be NULL in the error + * cleanup path of do_mmap_pgoff. When + * hugetlbfs ->mmap method fails, + * do_mmap_pgoff() nullifies vma->vm_file + * before calling this function to clean up. + * Since no pte has actually been setup, it is + * safe to do nothing in this case. + */ + if (vma->vm_file) { + unmap_hugepage_range(vma, start, end, NULL); + zap_work -= (end - start) / pages_per_huge_page(hstate_vma(vma)); + } + start = end; } else start = unmap_page_range(*tlbp, vma, -- cgit From a3437870160cf2caaac6bdd76c7377a5a4145a8c Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 23 Jul 2008 21:27:44 -0700 Subject: hugetlb: new sysfs interface Provide new hugepages user APIs that are more suited to multiple hstates in sysfs. There is a new directory, /sys/kernel/hugepages. Underneath that directory there will be a directory per-supported hugepage size, e.g.: /sys/kernel/hugepages/hugepages-64kB /sys/kernel/hugepages/hugepages-16384kB /sys/kernel/hugepages/hugepages-16777216kB corresponding to 64k, 16m and 16g respectively. Within each hugepages-size directory there are a number of files, corresponding to the tracked counters in the hstate, e.g.: /sys/kernel/hugepages/hugepages-64/nr_hugepages /sys/kernel/hugepages/hugepages-64/nr_overcommit_hugepages /sys/kernel/hugepages/hugepages-64/free_hugepages /sys/kernel/hugepages/hugepages-64/resv_hugepages /sys/kernel/hugepages/hugepages-64/surplus_hugepages Of these files, the first two are read-write and the latter three are read-only. The size of the hugepage being manipulated is trivially deducible from the enclosing directory and is always expressed in kB (to match meminfo). [dave@linux.vnet.ibm.com: fix build] [nacc@us.ibm.com: hugetlb: hang off of /sys/kernel/mm rather than /sys/kernel] [nacc@us.ibm.com: hugetlb: remove CONFIG_SYSFS dependency] Acked-by: Greg Kroah-Hartman Signed-off-by: Nishanth Aravamudan Signed-off-by: Nick Piggin Cc: Dave Hansen Signed-off-by: Nishanth Aravamudan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .../ABI/testing/sysfs-kernel-mm-hugepages | 15 ++ Documentation/vm/hugetlbpage.txt | 23 ++ include/linux/hugetlb.h | 2 + mm/hugetlb.c | 288 ++++++++++++++++----- 4 files changed, 262 insertions(+), 66 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-kernel-mm-hugepages (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-hugepages b/Documentation/ABI/testing/sysfs-kernel-mm-hugepages new file mode 100644 index 000000000000..e21c00571cf4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-mm-hugepages @@ -0,0 +1,15 @@ +What: /sys/kernel/mm/hugepages/ +Date: June 2008 +Contact: Nishanth Aravamudan , hugetlb maintainers +Description: + /sys/kernel/mm/hugepages/ contains a number of subdirectories + of the form hugepages-kB, where is the page size + of the hugepages supported by the kernel/CPU combination. + + Under these directories are a number of files: + nr_hugepages + nr_overcommit_hugepages + free_hugepages + surplus_hugepages + resv_hugepages + See Documentation/vm/hugetlbpage.txt for details. diff --git a/Documentation/vm/hugetlbpage.txt b/Documentation/vm/hugetlbpage.txt index 3102b81bef88..8a5b5763f0fe 100644 --- a/Documentation/vm/hugetlbpage.txt +++ b/Documentation/vm/hugetlbpage.txt @@ -95,6 +95,29 @@ this condition holds, however, no more surplus huge pages will be allowed on the system until one of the two sysctls are increased sufficiently, or the surplus huge pages go out of use and are freed. +With support for multiple hugepage pools at run-time available, much of +the hugepage userspace interface has been duplicated in sysfs. The above +information applies to the default hugepage size (which will be +controlled by the proc interfaces for backwards compatibility). The root +hugepage control directory is + + /sys/kernel/mm/hugepages + +For each hugepage size supported by the running kernel, a subdirectory +will exist, of the form + + hugepages-${size}kB + +Inside each of these directories, the same set of files will exist: + + nr_hugepages + nr_overcommit_hugepages + free_hugepages + resv_hugepages + surplus_hugepages + +which function as described above for the default hugepage-sized case. + If the user applications are going to request hugepages using mmap system call, then it is required that system administrator mount a file system of type hugetlbfs: diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index ba9263e631b9..58c0de32e7f0 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -164,6 +164,7 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, #ifdef CONFIG_HUGETLB_PAGE +#define HSTATE_NAME_LEN 32 /* Defines one hugetlb page size */ struct hstate { int hugetlb_next_nid; @@ -179,6 +180,7 @@ struct hstate { unsigned int nr_huge_pages_node[MAX_NUMNODES]; unsigned int free_huge_pages_node[MAX_NUMNODES]; unsigned int surplus_huge_pages_node[MAX_NUMNODES]; + char name[HSTATE_NAME_LEN]; }; void __init hugetlb_add_hstate(unsigned order); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 4cf7a90e9140..bb49ce5d0067 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -942,72 +943,6 @@ static void __init report_hugepages(void) } } -static int __init hugetlb_init(void) -{ - BUILD_BUG_ON(HPAGE_SHIFT == 0); - - if (!size_to_hstate(HPAGE_SIZE)) { - hugetlb_add_hstate(HUGETLB_PAGE_ORDER); - parsed_hstate->max_huge_pages = default_hstate_max_huge_pages; - } - default_hstate_idx = size_to_hstate(HPAGE_SIZE) - hstates; - - hugetlb_init_hstates(); - - report_hugepages(); - - return 0; -} -module_init(hugetlb_init); - -/* Should be called on processing a hugepagesz=... option */ -void __init hugetlb_add_hstate(unsigned order) -{ - struct hstate *h; - if (size_to_hstate(PAGE_SIZE << order)) { - printk(KERN_WARNING "hugepagesz= specified twice, ignoring\n"); - return; - } - BUG_ON(max_hstate >= HUGE_MAX_HSTATE); - BUG_ON(order == 0); - h = &hstates[max_hstate++]; - h->order = order; - h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1); - hugetlb_init_one_hstate(h); - parsed_hstate = h; -} - -static int __init hugetlb_setup(char *s) -{ - unsigned long *mhp; - - /* - * !max_hstate means we haven't parsed a hugepagesz= parameter yet, - * so this hugepages= parameter goes to the "default hstate". - */ - if (!max_hstate) - mhp = &default_hstate_max_huge_pages; - else - mhp = &parsed_hstate->max_huge_pages; - - if (sscanf(s, "%lu", mhp) <= 0) - *mhp = 0; - - return 1; -} -__setup("hugepages=", hugetlb_setup); - -static unsigned int cpuset_mems_nr(unsigned int *array) -{ - int node; - unsigned int nr = 0; - - for_each_node_mask(node, cpuset_current_mems_allowed) - nr += array[node]; - - return nr; -} - #ifdef CONFIG_SYSCTL #ifdef CONFIG_HIGHMEM static void try_to_free_low(struct hstate *h, unsigned long count) @@ -1105,6 +1040,227 @@ out: return ret; } +#define HSTATE_ATTR_RO(_name) \ + static struct kobj_attribute _name##_attr = __ATTR_RO(_name) + +#define HSTATE_ATTR(_name) \ + static struct kobj_attribute _name##_attr = \ + __ATTR(_name, 0644, _name##_show, _name##_store) + +static struct kobject *hugepages_kobj; +static struct kobject *hstate_kobjs[HUGE_MAX_HSTATE]; + +static struct hstate *kobj_to_hstate(struct kobject *kobj) +{ + int i; + for (i = 0; i < HUGE_MAX_HSTATE; i++) + if (hstate_kobjs[i] == kobj) + return &hstates[i]; + BUG(); + return NULL; +} + +static ssize_t nr_hugepages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hstate *h = kobj_to_hstate(kobj); + return sprintf(buf, "%lu\n", h->nr_huge_pages); +} +static ssize_t nr_hugepages_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int err; + unsigned long input; + struct hstate *h = kobj_to_hstate(kobj); + + err = strict_strtoul(buf, 10, &input); + if (err) + return 0; + + h->max_huge_pages = set_max_huge_pages(h, input); + + return count; +} +HSTATE_ATTR(nr_hugepages); + +static ssize_t nr_overcommit_hugepages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hstate *h = kobj_to_hstate(kobj); + return sprintf(buf, "%lu\n", h->nr_overcommit_huge_pages); +} +static ssize_t nr_overcommit_hugepages_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int err; + unsigned long input; + struct hstate *h = kobj_to_hstate(kobj); + + err = strict_strtoul(buf, 10, &input); + if (err) + return 0; + + spin_lock(&hugetlb_lock); + h->nr_overcommit_huge_pages = input; + spin_unlock(&hugetlb_lock); + + return count; +} +HSTATE_ATTR(nr_overcommit_hugepages); + +static ssize_t free_hugepages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hstate *h = kobj_to_hstate(kobj); + return sprintf(buf, "%lu\n", h->free_huge_pages); +} +HSTATE_ATTR_RO(free_hugepages); + +static ssize_t resv_hugepages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hstate *h = kobj_to_hstate(kobj); + return sprintf(buf, "%lu\n", h->resv_huge_pages); +} +HSTATE_ATTR_RO(resv_hugepages); + +static ssize_t surplus_hugepages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hstate *h = kobj_to_hstate(kobj); + return sprintf(buf, "%lu\n", h->surplus_huge_pages); +} +HSTATE_ATTR_RO(surplus_hugepages); + +static struct attribute *hstate_attrs[] = { + &nr_hugepages_attr.attr, + &nr_overcommit_hugepages_attr.attr, + &free_hugepages_attr.attr, + &resv_hugepages_attr.attr, + &surplus_hugepages_attr.attr, + NULL, +}; + +static struct attribute_group hstate_attr_group = { + .attrs = hstate_attrs, +}; + +static int __init hugetlb_sysfs_add_hstate(struct hstate *h) +{ + int retval; + + hstate_kobjs[h - hstates] = kobject_create_and_add(h->name, + hugepages_kobj); + if (!hstate_kobjs[h - hstates]) + return -ENOMEM; + + retval = sysfs_create_group(hstate_kobjs[h - hstates], + &hstate_attr_group); + if (retval) + kobject_put(hstate_kobjs[h - hstates]); + + return retval; +} + +static void __init hugetlb_sysfs_init(void) +{ + struct hstate *h; + int err; + + hugepages_kobj = kobject_create_and_add("hugepages", mm_kobj); + if (!hugepages_kobj) + return; + + for_each_hstate(h) { + err = hugetlb_sysfs_add_hstate(h); + if (err) + printk(KERN_ERR "Hugetlb: Unable to add hstate %s", + h->name); + } +} + +static void __exit hugetlb_exit(void) +{ + struct hstate *h; + + for_each_hstate(h) { + kobject_put(hstate_kobjs[h - hstates]); + } + + kobject_put(hugepages_kobj); +} +module_exit(hugetlb_exit); + +static int __init hugetlb_init(void) +{ + BUILD_BUG_ON(HPAGE_SHIFT == 0); + + if (!size_to_hstate(HPAGE_SIZE)) { + hugetlb_add_hstate(HUGETLB_PAGE_ORDER); + parsed_hstate->max_huge_pages = default_hstate_max_huge_pages; + } + default_hstate_idx = size_to_hstate(HPAGE_SIZE) - hstates; + + hugetlb_init_hstates(); + + report_hugepages(); + + hugetlb_sysfs_init(); + + return 0; +} +module_init(hugetlb_init); + +/* Should be called on processing a hugepagesz=... option */ +void __init hugetlb_add_hstate(unsigned order) +{ + struct hstate *h; + if (size_to_hstate(PAGE_SIZE << order)) { + printk(KERN_WARNING "hugepagesz= specified twice, ignoring\n"); + return; + } + BUG_ON(max_hstate >= HUGE_MAX_HSTATE); + BUG_ON(order == 0); + h = &hstates[max_hstate++]; + h->order = order; + h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1); + snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB", + huge_page_size(h)/1024); + hugetlb_init_one_hstate(h); + parsed_hstate = h; +} + +static int __init hugetlb_setup(char *s) +{ + unsigned long *mhp; + + /* + * !max_hstate means we haven't parsed a hugepagesz= parameter yet, + * so this hugepages= parameter goes to the "default hstate". + */ + if (!max_hstate) + mhp = &default_hstate_max_huge_pages; + else + mhp = &parsed_hstate->max_huge_pages; + + if (sscanf(s, "%lu", mhp) <= 0) + *mhp = 0; + + return 1; +} +__setup("hugepages=", hugetlb_setup); + +static unsigned int cpuset_mems_nr(unsigned int *array) +{ + int node; + unsigned int nr = 0; + + for_each_node_mask(node, cpuset_current_mems_allowed) + nr += array[node]; + + return nr; +} + int hugetlb_sysctl_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length, loff_t *ppos) -- cgit From b54bbf7b81170f03597c17dd0b559e3006bc9868 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 23 Jul 2008 21:27:45 -0700 Subject: mm: introduce non panic alloc_bootmem Straight forward variant of the existing __alloc_bootmem_node, only subsequent patch when allocating giant hugepages at boot -- don't want to panic if we can't allocate as many as the user asked for. Signed-off-by: Andi Kleen Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 4 ++++ mm/bootmem.c | 12 ++++++++++++ 2 files changed, 16 insertions(+) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index dd8fee6c46d9..f352c5f125b4 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -89,6 +89,10 @@ extern void *__alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal); +extern void *__alloc_bootmem_node_nopanic(pg_data_t *pgdat, + unsigned long size, + unsigned long align, + unsigned long goal); extern unsigned long init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, unsigned long startpfn, diff --git a/mm/bootmem.c b/mm/bootmem.c index 4bc6ae2fbaa3..9ac972535fff 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -578,6 +578,18 @@ void * __init alloc_bootmem_section(unsigned long size, } #endif +void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, + unsigned long align, unsigned long goal) +{ + void *ptr; + + ptr = alloc_bootmem_core(pgdat->bdata, size, align, goal, 0); + if (ptr) + return ptr; + + return __alloc_bootmem_nopanic(size, align, goal); +} + #ifndef ARCH_LOW_ADDRESS_LIMIT #define ARCH_LOW_ADDRESS_LIMIT 0xffffffffUL #endif -- cgit From ceb868796181dc95ea01a110e123afd391639873 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 23 Jul 2008 21:27:50 -0700 Subject: hugetlb: introduce pud_huge Straight forward extensions for huge pages located in the PUD instead of PMDs. Signed-off-by: Andi Kleen Signed-off-by: Nick Piggin Cc: Martin Schwidefsky Cc: Heiko Carstens Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/mm/hugetlbpage.c | 6 ++++++ arch/powerpc/mm/hugetlbpage.c | 5 +++++ arch/s390/mm/hugetlbpage.c | 5 +++++ arch/sh/mm/hugetlbpage.c | 5 +++++ arch/sparc64/mm/hugetlbpage.c | 5 +++++ arch/x86/mm/hugetlbpage.c | 25 ++++++++++++++++++++++++- include/linux/hugetlb.h | 5 +++++ mm/hugetlb.c | 9 +++++++++ mm/memory.c | 15 +++++++++++---- 9 files changed, 75 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/mm/hugetlbpage.c b/arch/ia64/mm/hugetlbpage.c index 6170f097d255..c45fc7f5a979 100644 --- a/arch/ia64/mm/hugetlbpage.c +++ b/arch/ia64/mm/hugetlbpage.c @@ -107,6 +107,12 @@ int pmd_huge(pmd_t pmd) { return 0; } + +int pud_huge(pud_t pud) +{ + return 0; +} + struct page * follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) { diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index c94dc71af989..63db7adce717 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -369,6 +369,11 @@ int pmd_huge(pmd_t pmd) return 0; } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page * follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index 9162dc84f77f..f28c43d2f61d 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -120,6 +120,11 @@ int pmd_huge(pmd_t pmd) return !!(pmd_val(pmd) & _SEGMENT_ENTRY_LARGE); } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmdp, int write) { diff --git a/arch/sh/mm/hugetlbpage.c b/arch/sh/mm/hugetlbpage.c index 2f9dbe0ef4ac..9304117039c4 100644 --- a/arch/sh/mm/hugetlbpage.c +++ b/arch/sh/mm/hugetlbpage.c @@ -79,6 +79,11 @@ int pmd_huge(pmd_t pmd) return 0; } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) { diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c index 1307b23f6a76..f27d10369e0c 100644 --- a/arch/sparc64/mm/hugetlbpage.c +++ b/arch/sparc64/mm/hugetlbpage.c @@ -295,6 +295,11 @@ int pmd_huge(pmd_t pmd) return 0; } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) { diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c index 52476fde8996..a4789e87a315 100644 --- a/arch/x86/mm/hugetlbpage.c +++ b/arch/x86/mm/hugetlbpage.c @@ -189,6 +189,11 @@ int pmd_huge(pmd_t pmd) return 0; } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page * follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) @@ -209,6 +214,11 @@ int pmd_huge(pmd_t pmd) return !!(pmd_val(pmd) & _PAGE_PSE); } +int pud_huge(pud_t pud) +{ + return 0; +} + struct page * follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write) @@ -217,9 +227,22 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address, page = pte_page(*(pte_t *)pmd); if (page) - page += ((address & ~HPAGE_MASK) >> PAGE_SHIFT); + page += ((address & ~PMD_MASK) >> PAGE_SHIFT); return page; } + +struct page * +follow_huge_pud(struct mm_struct *mm, unsigned long address, + pud_t *pud, int write) +{ + struct page *page; + + page = pte_page(*(pte_t *)pud); + if (page) + page += ((address & ~PUD_MASK) >> PAGE_SHIFT); + return page; +} + #endif /* x86_64 also uses this file */ diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 58c0de32e7f0..b2c17f62cacb 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -50,7 +50,10 @@ struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, int write); struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, pmd_t *pmd, int write); +struct page *follow_huge_pud(struct mm_struct *mm, unsigned long address, + pud_t *pud, int write); int pmd_huge(pmd_t pmd); +int pud_huge(pud_t pmd); void hugetlb_change_protection(struct vm_area_struct *vma, unsigned long address, unsigned long end, pgprot_t newprot); @@ -78,8 +81,10 @@ static inline unsigned long hugetlb_total_pages(void) #define hugetlb_report_meminfo(buf) 0 #define hugetlb_report_node_meminfo(n, buf) 0 #define follow_huge_pmd(mm, addr, pmd, write) NULL +#define follow_huge_pud(mm, addr, pud, write) NULL #define prepare_hugepage_range(file, addr, len) (-EINVAL) #define pmd_huge(x) 0 +#define pud_huge(x) 0 #define is_hugepage_only_range(mm, addr, len) 0 #define hugetlb_free_pgd_range(tlb, addr, end, floor, ceiling) ({BUG(); 0; }) #define hugetlb_fault(mm, vma, addr, write) ({ BUG(); 0; }) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 0c74c14dd2f7..107c1ce223cb 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1996,6 +1996,15 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, return ret; } +/* Can be overriden by architectures */ +__attribute__((weak)) struct page * +follow_huge_pud(struct mm_struct *mm, unsigned long address, + pud_t *pud, int write) +{ + BUG(); + return NULL; +} + int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, struct page **pages, struct vm_area_struct **vmas, unsigned long *position, int *length, int i, diff --git a/mm/memory.c b/mm/memory.c index 02fc6b1047b0..262e3eb6601a 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -998,19 +998,24 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address, goto no_page_table; pud = pud_offset(pgd, address); - if (pud_none(*pud) || unlikely(pud_bad(*pud))) + if (pud_none(*pud)) + goto no_page_table; + if (pud_huge(*pud)) { + BUG_ON(flags & FOLL_GET); + page = follow_huge_pud(mm, address, pud, flags & FOLL_WRITE); + goto out; + } + if (unlikely(pud_bad(*pud))) goto no_page_table; - + pmd = pmd_offset(pud, address); if (pmd_none(*pmd)) goto no_page_table; - if (pmd_huge(*pmd)) { BUG_ON(flags & FOLL_GET); page = follow_huge_pmd(mm, address, pmd, flags & FOLL_WRITE); goto out; } - if (unlikely(pmd_bad(*pmd))) goto no_page_table; @@ -1567,6 +1572,8 @@ static int apply_to_pmd_range(struct mm_struct *mm, pud_t *pud, unsigned long next; int err; + BUG_ON(pud_huge(*pud)); + pmd = pmd_alloc(mm, pud, addr); if (!pmd) return -ENOMEM; -- cgit From 53ba51d21d6e048424ab8aadfebdb1f25ae07b60 Mon Sep 17 00:00:00 2001 From: Jon Tollefson Date: Wed, 23 Jul 2008 21:27:52 -0700 Subject: hugetlb: allow arch overridden hugepage allocation Allow alloc_bootmem_huge_page() to be overridden by architectures that can't always use bootmem. This requires huge_boot_pages to be available for use by this function. This is required for powerpc 16G pages, which have to be reserved prior to boot-time. The location of these pages are indicated in the device tree. Acked-by: Adam Litke Signed-off-by: Jon Tollefson Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 10 ++++++++++ mm/hugetlb.c | 11 +++-------- 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index b2c17f62cacb..9a71d4cc88c8 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -39,6 +39,7 @@ void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed); extern unsigned long hugepages_treat_as_movable; extern const unsigned long hugetlb_zero, hugetlb_infinity; extern int sysctl_hugetlb_shm_group; +extern struct list_head huge_boot_pages; /* arch callbacks */ @@ -188,6 +189,14 @@ struct hstate { char name[HSTATE_NAME_LEN]; }; +struct huge_bootmem_page { + struct list_head list; + struct hstate *hstate; +}; + +/* arch callback */ +int __init alloc_bootmem_huge_page(struct hstate *h); + void __init hugetlb_add_hstate(unsigned order); struct hstate *size_to_hstate(unsigned long size); @@ -256,6 +265,7 @@ static inline struct hstate *page_hstate(struct page *page) #else struct hstate {}; +#define alloc_bootmem_huge_page(h) NULL #define hstate_file(f) NULL #define hstate_vma(v) NULL #define hstate_inode(i) NULL diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 2a2f6e869401..3e1506b808a3 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -31,6 +31,8 @@ static int max_hstate; unsigned int default_hstate_idx; struct hstate hstates[HUGE_MAX_HSTATE]; +__initdata LIST_HEAD(huge_boot_pages); + /* for command line parsing */ static struct hstate * __initdata parsed_hstate; static unsigned long __initdata default_hstate_max_huge_pages; @@ -925,14 +927,7 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, return page; } -static __initdata LIST_HEAD(huge_boot_pages); - -struct huge_bootmem_page { - struct list_head list; - struct hstate *hstate; -}; - -static int __init alloc_bootmem_huge_page(struct hstate *h) +__attribute__((weak)) int alloc_bootmem_huge_page(struct hstate *h) { struct huge_bootmem_page *m; int nr_nodes = nodes_weight(node_online_map); -- cgit From 223e8dc9249c9e15f6c8b638d73fcad78ccb0a88 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:28:00 -0700 Subject: bootmem: reorder code to match new bootmem structure This only reorders functions so that further patches will be easier to read. No code changed. Signed-off-by: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 86 ++++++------ mm/bootmem.c | 356 ++++++++++++++++++++++++------------------------ 2 files changed, 222 insertions(+), 220 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index f352c5f125b4..5000fd70b04f 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -41,36 +41,62 @@ typedef struct bootmem_data { extern bootmem_data_t bootmem_node_data[]; extern unsigned long bootmem_bootmap_pages(unsigned long); + +extern unsigned long init_bootmem_node(pg_data_t *pgdat, + unsigned long freepfn, + unsigned long startpfn, + unsigned long endpfn); extern unsigned long init_bootmem(unsigned long addr, unsigned long memend); + +extern unsigned long free_all_bootmem_node(pg_data_t *pgdat); +extern unsigned long free_all_bootmem(void); + +extern void free_bootmem_node(pg_data_t *pgdat, + unsigned long addr, + unsigned long size); extern void free_bootmem(unsigned long addr, unsigned long size); -extern void *__alloc_bootmem(unsigned long size, + +/* + * Flags for reserve_bootmem (also if CONFIG_HAVE_ARCH_BOOTMEM_NODE, + * the architecture-specific code should honor this). + * + * If flags is 0, then the return value is always 0 (success). If + * flags contains BOOTMEM_EXCLUSIVE, then -EBUSY is returned if the + * memory already was reserved. + */ +#define BOOTMEM_DEFAULT 0 +#define BOOTMEM_EXCLUSIVE (1<<0) + +extern int reserve_bootmem_node(pg_data_t *pgdat, + unsigned long physaddr, + unsigned long size, + int flags); +#ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE +extern int reserve_bootmem(unsigned long addr, unsigned long size, int flags); +#endif + +extern void *__alloc_bootmem_nopanic(unsigned long size, unsigned long align, unsigned long goal); -extern void *__alloc_bootmem_nopanic(unsigned long size, +extern void *__alloc_bootmem(unsigned long size, unsigned long align, unsigned long goal); extern void *__alloc_bootmem_low(unsigned long size, unsigned long align, unsigned long goal); +extern void *__alloc_bootmem_node(pg_data_t *pgdat, + unsigned long size, + unsigned long align, + unsigned long goal); +extern void *__alloc_bootmem_node_nopanic(pg_data_t *pgdat, + unsigned long size, + unsigned long align, + unsigned long goal); extern void *__alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal); - -/* - * flags for reserve_bootmem (also if CONFIG_HAVE_ARCH_BOOTMEM_NODE, - * the architecture-specific code should honor this) - */ -#define BOOTMEM_DEFAULT 0 -#define BOOTMEM_EXCLUSIVE (1<<0) - #ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE -/* - * If flags is 0, then the return value is always 0 (success). If - * flags contains BOOTMEM_EXCLUSIVE, then -EBUSY is returned if the - * memory already was reserved. - */ -extern int reserve_bootmem(unsigned long addr, unsigned long size, int flags); #define alloc_bootmem(x) \ __alloc_bootmem(x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low(x) \ @@ -83,38 +109,16 @@ extern int reserve_bootmem(unsigned long addr, unsigned long size, int flags); extern int reserve_bootmem_generic(unsigned long addr, unsigned long size, int flags); -extern unsigned long free_all_bootmem(void); -extern unsigned long free_all_bootmem_node(pg_data_t *pgdat); -extern void *__alloc_bootmem_node(pg_data_t *pgdat, - unsigned long size, - unsigned long align, - unsigned long goal); -extern void *__alloc_bootmem_node_nopanic(pg_data_t *pgdat, - unsigned long size, - unsigned long align, - unsigned long goal); -extern unsigned long init_bootmem_node(pg_data_t *pgdat, - unsigned long freepfn, - unsigned long startpfn, - unsigned long endpfn); -extern int reserve_bootmem_node(pg_data_t *pgdat, - unsigned long physaddr, - unsigned long size, - int flags); -extern void free_bootmem_node(pg_data_t *pgdat, - unsigned long addr, - unsigned long size); -extern void *alloc_bootmem_section(unsigned long size, - unsigned long section_nr); -#ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE #define alloc_bootmem_node(pgdat, x) \ __alloc_bootmem_node(pgdat, x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_pages_node(pgdat, x) \ __alloc_bootmem_node(pgdat, x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low_pages_node(pgdat, x) \ __alloc_bootmem_low_node(pgdat, x, PAGE_SIZE, 0) -#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ + +extern void *alloc_bootmem_section(unsigned long size, + unsigned long section_nr); #ifdef CONFIG_HAVE_ARCH_ALLOC_REMAP extern void *alloc_remap(int nid, unsigned long size); diff --git a/mm/bootmem.c b/mm/bootmem.c index 9ac972535fff..24eacf52c50e 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -38,6 +38,19 @@ unsigned long saved_max_pfn; bootmem_data_t bootmem_node_data[MAX_NUMNODES] __initdata; +/* + * Given an initialised bdata, it returns the size of the boot bitmap + */ +static unsigned long __init get_mapsize(bootmem_data_t *bdata) +{ + unsigned long mapsize; + unsigned long start = PFN_DOWN(bdata->node_boot_start); + unsigned long end = bdata->node_low_pfn; + + mapsize = ((end - start) + 7) / 8; + return ALIGN(mapsize, sizeof(long)); +} + /* return the number of _pages_ that will be allocated for the boot bitmap */ unsigned long __init bootmem_bootmap_pages(unsigned long pages) { @@ -71,19 +84,6 @@ static void __init link_bootmem(bootmem_data_t *bdata) list_add_tail(&bdata->list, &bdata_list); } -/* - * Given an initialised bdata, it returns the size of the boot bitmap - */ -static unsigned long __init get_mapsize(bootmem_data_t *bdata) -{ - unsigned long mapsize; - unsigned long start = PFN_DOWN(bdata->node_boot_start); - unsigned long end = bdata->node_low_pfn; - - mapsize = ((end - start) + 7) / 8; - return ALIGN(mapsize, sizeof(long)); -} - /* * Called once to set up the allocator itself. */ @@ -108,6 +108,146 @@ static unsigned long __init init_bootmem_core(bootmem_data_t *bdata, return mapsize; } +unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, + unsigned long startpfn, unsigned long endpfn) +{ + return init_bootmem_core(pgdat->bdata, freepfn, startpfn, endpfn); +} + +unsigned long __init init_bootmem(unsigned long start, unsigned long pages) +{ + max_low_pfn = pages; + min_low_pfn = start; + return init_bootmem_core(NODE_DATA(0)->bdata, start, 0, pages); +} + +static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) +{ + struct page *page; + unsigned long pfn; + unsigned long i, count; + unsigned long idx; + unsigned long *map; + int gofast = 0; + + BUG_ON(!bdata->node_bootmem_map); + + count = 0; + /* first extant page of the node */ + pfn = PFN_DOWN(bdata->node_boot_start); + idx = bdata->node_low_pfn - pfn; + map = bdata->node_bootmem_map; + /* + * Check if we are aligned to BITS_PER_LONG pages. If so, we might + * be able to free page orders of that size at once. + */ + if (!(pfn & (BITS_PER_LONG-1))) + gofast = 1; + + for (i = 0; i < idx; ) { + unsigned long v = ~map[i / BITS_PER_LONG]; + + if (gofast && v == ~0UL) { + int order; + + page = pfn_to_page(pfn); + count += BITS_PER_LONG; + order = ffs(BITS_PER_LONG) - 1; + __free_pages_bootmem(page, order); + i += BITS_PER_LONG; + page += BITS_PER_LONG; + } else if (v) { + unsigned long m; + + page = pfn_to_page(pfn); + for (m = 1; m && i < idx; m<<=1, page++, i++) { + if (v & m) { + count++; + __free_pages_bootmem(page, 0); + } + } + } else { + i += BITS_PER_LONG; + } + pfn += BITS_PER_LONG; + } + + /* + * Now free the allocator bitmap itself, it's not + * needed anymore: + */ + page = virt_to_page(bdata->node_bootmem_map); + idx = (get_mapsize(bdata) + PAGE_SIZE-1) >> PAGE_SHIFT; + for (i = 0; i < idx; i++, page++) + __free_pages_bootmem(page, 0); + count += i; + bdata->node_bootmem_map = NULL; + + return count; +} + +unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) +{ + register_page_bootmem_info_node(pgdat); + return free_all_bootmem_core(pgdat->bdata); +} + +unsigned long __init free_all_bootmem(void) +{ + return free_all_bootmem_core(NODE_DATA(0)->bdata); +} + +static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, + unsigned long size) +{ + unsigned long sidx, eidx; + unsigned long i; + + BUG_ON(!size); + + /* out range */ + if (addr + size < bdata->node_boot_start || + PFN_DOWN(addr) > bdata->node_low_pfn) + return; + /* + * round down end of usable mem, partially free pages are + * considered reserved. + */ + + if (addr >= bdata->node_boot_start && addr < bdata->last_success) + bdata->last_success = addr; + + /* + * Round up to index to the range. + */ + if (PFN_UP(addr) > PFN_DOWN(bdata->node_boot_start)) + sidx = PFN_UP(addr) - PFN_DOWN(bdata->node_boot_start); + else + sidx = 0; + + eidx = PFN_DOWN(addr + size - bdata->node_boot_start); + if (eidx > bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start)) + eidx = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); + + for (i = sidx; i < eidx; i++) { + if (unlikely(!test_and_clear_bit(i, bdata->node_bootmem_map))) + BUG(); + } +} + +void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, + unsigned long size) +{ + free_bootmem_core(pgdat->bdata, physaddr, size); +} + +void __init free_bootmem(unsigned long addr, unsigned long size) +{ + bootmem_data_t *bdata; + list_for_each_entry(bdata, &bdata_list, list) + free_bootmem_core(bdata, addr, size); +} + /* * Marks a particular physical memory range as unallocatable. Usable RAM * might be used for boot-time allocations - or it might get added @@ -183,43 +323,36 @@ static void __init reserve_bootmem_core(bootmem_data_t *bdata, } } -static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, - unsigned long size) +int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, + unsigned long size, int flags) { - unsigned long sidx, eidx; - unsigned long i; - - BUG_ON(!size); - - /* out range */ - if (addr + size < bdata->node_boot_start || - PFN_DOWN(addr) > bdata->node_low_pfn) - return; - /* - * round down end of usable mem, partially free pages are - * considered reserved. - */ - - if (addr >= bdata->node_boot_start && addr < bdata->last_success) - bdata->last_success = addr; + int ret; - /* - * Round up to index to the range. - */ - if (PFN_UP(addr) > PFN_DOWN(bdata->node_boot_start)) - sidx = PFN_UP(addr) - PFN_DOWN(bdata->node_boot_start); - else - sidx = 0; + ret = can_reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); + if (ret < 0) + return -ENOMEM; + reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); + return 0; +} - eidx = PFN_DOWN(addr + size - bdata->node_boot_start); - if (eidx > bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start)) - eidx = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); +#ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE +int __init reserve_bootmem(unsigned long addr, unsigned long size, + int flags) +{ + bootmem_data_t *bdata; + int ret; - for (i = sidx; i < eidx; i++) { - if (unlikely(!test_and_clear_bit(i, bdata->node_bootmem_map))) - BUG(); + list_for_each_entry(bdata, &bdata_list, list) { + ret = can_reserve_bootmem_core(bdata, addr, size, flags); + if (ret < 0) + return ret; } + list_for_each_entry(bdata, &bdata_list, list) + reserve_bootmem_core(bdata, addr, size, flags); + + return 0; } +#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ /* * We 'merge' subsequent allocations to save space. We might 'lose' @@ -371,140 +504,6 @@ found: return ret; } -static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) -{ - struct page *page; - unsigned long pfn; - unsigned long i, count; - unsigned long idx; - unsigned long *map; - int gofast = 0; - - BUG_ON(!bdata->node_bootmem_map); - - count = 0; - /* first extant page of the node */ - pfn = PFN_DOWN(bdata->node_boot_start); - idx = bdata->node_low_pfn - pfn; - map = bdata->node_bootmem_map; - /* - * Check if we are aligned to BITS_PER_LONG pages. If so, we might - * be able to free page orders of that size at once. - */ - if (!(pfn & (BITS_PER_LONG-1))) - gofast = 1; - - for (i = 0; i < idx; ) { - unsigned long v = ~map[i / BITS_PER_LONG]; - - if (gofast && v == ~0UL) { - int order; - - page = pfn_to_page(pfn); - count += BITS_PER_LONG; - order = ffs(BITS_PER_LONG) - 1; - __free_pages_bootmem(page, order); - i += BITS_PER_LONG; - page += BITS_PER_LONG; - } else if (v) { - unsigned long m; - - page = pfn_to_page(pfn); - for (m = 1; m && i < idx; m<<=1, page++, i++) { - if (v & m) { - count++; - __free_pages_bootmem(page, 0); - } - } - } else { - i += BITS_PER_LONG; - } - pfn += BITS_PER_LONG; - } - - /* - * Now free the allocator bitmap itself, it's not - * needed anymore: - */ - page = virt_to_page(bdata->node_bootmem_map); - idx = (get_mapsize(bdata) + PAGE_SIZE-1) >> PAGE_SHIFT; - for (i = 0; i < idx; i++, page++) - __free_pages_bootmem(page, 0); - count += i; - bdata->node_bootmem_map = NULL; - - return count; -} - -unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, - unsigned long startpfn, unsigned long endpfn) -{ - return init_bootmem_core(pgdat->bdata, freepfn, startpfn, endpfn); -} - -int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, - unsigned long size, int flags) -{ - int ret; - - ret = can_reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); - if (ret < 0) - return -ENOMEM; - reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); - - return 0; -} - -void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, - unsigned long size) -{ - free_bootmem_core(pgdat->bdata, physaddr, size); -} - -unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) -{ - register_page_bootmem_info_node(pgdat); - return free_all_bootmem_core(pgdat->bdata); -} - -unsigned long __init init_bootmem(unsigned long start, unsigned long pages) -{ - max_low_pfn = pages; - min_low_pfn = start; - return init_bootmem_core(NODE_DATA(0)->bdata, start, 0, pages); -} - -#ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE -int __init reserve_bootmem(unsigned long addr, unsigned long size, - int flags) -{ - bootmem_data_t *bdata; - int ret; - - list_for_each_entry(bdata, &bdata_list, list) { - ret = can_reserve_bootmem_core(bdata, addr, size, flags); - if (ret < 0) - return ret; - } - list_for_each_entry(bdata, &bdata_list, list) - reserve_bootmem_core(bdata, addr, size, flags); - - return 0; -} -#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ - -void __init free_bootmem(unsigned long addr, unsigned long size) -{ - bootmem_data_t *bdata; - list_for_each_entry(bdata, &bdata_list, list) - free_bootmem_core(bdata, addr, size); -} - -unsigned long __init free_all_bootmem(void) -{ - return free_all_bootmem_core(NODE_DATA(0)->bdata); -} - void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align, unsigned long goal) { @@ -534,7 +533,6 @@ void * __init __alloc_bootmem(unsigned long size, unsigned long align, return NULL; } - void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal) { -- cgit From 5f2809e69c7128f86316048221cf45146f69a4a0 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:28:05 -0700 Subject: bootmem: clean up alloc_bootmem_core alloc_bootmem_core has become quite nasty to read over time. This is a clean rewrite that keeps the semantics. bdata->last_pos has been dropped. bdata->last_success has been renamed to hint_idx and it is now an index relative to the node's range. Since further block searching might start at this index, it is now set to the end of a succeeded allocation rather than its beginning. bdata->last_offset has been renamed to last_end_off to be more clear that it represents the ending address of the last allocation relative to the node. [y-goto@jp.fujitsu.com: fix new alloc_bootmem_core()] Signed-off-by: Johannes Weiner Signed-off-by: Yasunori Goto Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 6 +- mm/bootmem.c | 212 +++++++++++++++++------------------------------- 2 files changed, 78 insertions(+), 140 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 5000fd70b04f..90921d10ffa2 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -31,10 +31,8 @@ typedef struct bootmem_data { unsigned long node_boot_start; unsigned long node_low_pfn; void *node_bootmem_map; - unsigned long last_offset; - unsigned long last_pos; - unsigned long last_success; /* Previous allocation point. To speed - * up searching */ + unsigned long last_end_off; + unsigned long hint_idx; struct list_head list; } bootmem_data_t; diff --git a/mm/bootmem.c b/mm/bootmem.c index 300d126ec533..94ea612deccf 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -242,8 +242,9 @@ static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, * considered reserved. */ - if (addr >= bdata->node_boot_start && addr < bdata->last_success) - bdata->last_success = addr; + if (addr >= bdata->node_boot_start && + PFN_DOWN(addr - bdata->node_boot_start) < bdata->hint_idx) + bdata->hint_idx = PFN_DOWN(addr - bdata->node_boot_start); /* * Round up to index to the range. @@ -431,36 +432,16 @@ int __init reserve_bootmem(unsigned long addr, unsigned long size, } #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ -/* - * We 'merge' subsequent allocations to save space. We might 'lose' - * some fraction of a page if allocations cannot be satisfied due to - * size constraints on boxes where there is physical RAM space - * fragmentation - in these cases (mostly large memory boxes) this - * is not a problem. - * - * On low memory boxes we get it right in 100% of the cases. - * - * alignment has to be a power of 2 value. - * - * NOTE: This function is _not_ reentrant. - */ -static void * __init -alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, - unsigned long align, unsigned long goal, unsigned long limit) +static void * __init alloc_bootmem_core(struct bootmem_data *bdata, + unsigned long size, unsigned long align, + unsigned long goal, unsigned long limit) { - unsigned long areasize, preferred; - unsigned long i, start = 0, incr, eidx, end_pfn; - void *ret; - unsigned long node_boot_start; - void *node_bootmem_map; - - if (!size) { - printk("alloc_bootmem_core(): zero-sized request\n"); - BUG(); - } - BUG_ON(align & (align-1)); + unsigned long min, max, start, sidx, midx, step; + + BUG_ON(!size); + BUG_ON(align & (align - 1)); + BUG_ON(limit && goal + size > limit); - /* on nodes without memory - bootmem_map is NULL */ if (!bdata->node_bootmem_map) return NULL; @@ -468,126 +449,85 @@ alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, bdata - bootmem_node_data, size, PAGE_ALIGN(size) >> PAGE_SHIFT, align, goal, limit); - /* bdata->node_boot_start is supposed to be (12+6)bits alignment on x86_64 ? */ - node_boot_start = bdata->node_boot_start; - node_bootmem_map = bdata->node_bootmem_map; - if (align) { - node_boot_start = ALIGN(bdata->node_boot_start, align); - if (node_boot_start > bdata->node_boot_start) - node_bootmem_map = (unsigned long *)bdata->node_bootmem_map + - PFN_DOWN(node_boot_start - bdata->node_boot_start)/BITS_PER_LONG; - } + min = PFN_DOWN(bdata->node_boot_start); + max = bdata->node_low_pfn; - if (limit && node_boot_start >= limit) + goal >>= PAGE_SHIFT; + limit >>= PAGE_SHIFT; + + if (limit && max > limit) + max = limit; + if (max <= min) return NULL; - end_pfn = bdata->node_low_pfn; - limit = PFN_DOWN(limit); - if (limit && end_pfn > limit) - end_pfn = limit; + step = max(align >> PAGE_SHIFT, 1UL); - eidx = end_pfn - PFN_DOWN(node_boot_start); + if (goal && min < goal && goal < max) + start = ALIGN(goal, step); + else + start = ALIGN(min, step); - /* - * We try to allocate bootmem pages above 'goal' - * first, then we try to allocate lower pages. - */ - preferred = 0; - if (goal && PFN_DOWN(goal) < end_pfn) { - if (goal > node_boot_start) - preferred = goal - node_boot_start; - - if (bdata->last_success > node_boot_start && - bdata->last_success - node_boot_start >= preferred) - if (!limit || (limit && limit > bdata->last_success)) - preferred = bdata->last_success - node_boot_start; - } + sidx = start - PFN_DOWN(bdata->node_boot_start); + midx = max - PFN_DOWN(bdata->node_boot_start); - preferred = PFN_DOWN(ALIGN(preferred, align)); - areasize = (size + PAGE_SIZE-1) / PAGE_SIZE; - incr = align >> PAGE_SHIFT ? : 1; + if (bdata->hint_idx > sidx) { + /* Make sure we retry on failure */ + goal = 1; + sidx = ALIGN(bdata->hint_idx, step); + } -restart_scan: - for (i = preferred; i < eidx;) { - unsigned long j; + while (1) { + int merge; + void *region; + unsigned long eidx, i, start_off, end_off; +find_block: + sidx = find_next_zero_bit(bdata->node_bootmem_map, midx, sidx); + sidx = ALIGN(sidx, step); + eidx = sidx + PFN_UP(size); - i = find_next_zero_bit(node_bootmem_map, eidx, i); - i = ALIGN(i, incr); - if (i >= eidx) + if (sidx >= midx || eidx > midx) break; - if (test_bit(i, node_bootmem_map)) { - i += incr; - continue; - } - for (j = i + 1; j < i + areasize; ++j) { - if (j >= eidx) - goto fail_block; - if (test_bit(j, node_bootmem_map)) - goto fail_block; - } - start = i; - goto found; - fail_block: - i = ALIGN(j, incr); - if (i == j) - i += incr; - } - - if (preferred > 0) { - preferred = 0; - goto restart_scan; - } - return NULL; -found: - bdata->last_success = PFN_PHYS(start) + node_boot_start; - BUG_ON(start >= eidx); + for (i = sidx; i < eidx; i++) + if (test_bit(i, bdata->node_bootmem_map)) { + sidx = ALIGN(i, step); + if (sidx == i) + sidx += step; + goto find_block; + } - /* - * Is the next page of the previous allocation-end the start - * of this allocation's buffer? If yes then we can 'merge' - * the previous partial page with this allocation. - */ - if (align < PAGE_SIZE && - bdata->last_offset && bdata->last_pos+1 == start) { - unsigned long offset, remaining_size; - offset = ALIGN(bdata->last_offset, align); - BUG_ON(offset > PAGE_SIZE); - remaining_size = PAGE_SIZE - offset; - if (size < remaining_size) { - areasize = 0; - /* last_pos unchanged */ - bdata->last_offset = offset + size; - ret = phys_to_virt(bdata->last_pos * PAGE_SIZE + - offset + node_boot_start); - } else { - remaining_size = size - remaining_size; - areasize = (remaining_size + PAGE_SIZE-1) / PAGE_SIZE; - ret = phys_to_virt(bdata->last_pos * PAGE_SIZE + - offset + node_boot_start); - bdata->last_pos = start + areasize - 1; - bdata->last_offset = remaining_size; - } - bdata->last_offset &= ~PAGE_MASK; - } else { - bdata->last_pos = start + areasize - 1; - bdata->last_offset = size & ~PAGE_MASK; - ret = phys_to_virt(start * PAGE_SIZE + node_boot_start); + if (bdata->last_end_off && + PFN_DOWN(bdata->last_end_off) + 1 == sidx) + start_off = ALIGN(bdata->last_end_off, align); + else + start_off = PFN_PHYS(sidx); + + merge = PFN_DOWN(start_off) < sidx; + end_off = start_off + size; + + bdata->last_end_off = end_off; + bdata->hint_idx = PFN_UP(end_off); + + /* + * Reserve the area now: + */ + for (i = PFN_DOWN(start_off) + merge; + i < PFN_UP(end_off); i++) + if (test_and_set_bit(i, bdata->node_bootmem_map)) + BUG(); + + region = phys_to_virt(bdata->node_boot_start + start_off); + memset(region, 0, size); + return region; } - bdebug("nid=%td start=%lx end=%lx\n", - bdata - bootmem_node_data, - start + PFN_DOWN(bdata->node_boot_start), - start + areasize + PFN_DOWN(bdata->node_boot_start)); + if (goal) { + goal = 0; + sidx = 0; + goto find_block; + } - /* - * Reserve the area now: - */ - for (i = start; i < start + areasize; i++) - if (unlikely(test_and_set_bit(i, node_bootmem_map))) - BUG(); - memset(ret, 0, size); - return ret; + return NULL; } /** -- cgit From 3560e249abda6bee41a07a7bf0383a6e193e2839 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 23 Jul 2008 21:28:09 -0700 Subject: bootmem: replace node_boot_start in struct bootmem_data Almost all users of this field need a PFN instead of a physical address, so replace node_boot_start with node_min_pfn. [Lee.Schermerhorn@hp.com: fix spurious BUG_ON() in mark_bootmem()] Signed-off-by: Johannes Weiner Cc: Signed-off-by: Lee Schermerhorn Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/mm/numa.c | 2 +- arch/arm/plat-omap/fb.c | 4 +--- arch/avr32/mm/init.c | 3 +-- arch/ia64/mm/discontig.c | 19 ++++++++++--------- arch/m32r/mm/discontig.c | 3 +-- arch/m32r/mm/init.c | 4 +--- arch/mn10300/mm/init.c | 6 +++--- arch/sh/mm/init.c | 2 +- include/linux/bootmem.h | 2 +- mm/bootmem.c | 40 +++++++++++++++++++++------------------- 10 files changed, 41 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index def0c74a78a8..d8c4ceaf00b9 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -304,7 +304,7 @@ void __init paging_init(void) for_each_online_node(nid) { bootmem_data_t *bdata = &bootmem_node_data[nid]; - unsigned long start_pfn = bdata->node_boot_start >> PAGE_SHIFT; + unsigned long start_pfn = bdata->node_min_pfn; unsigned long end_pfn = bdata->node_low_pfn; if (dma_local_pfn >= end_pfn - start_pfn) diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 7854f19b77cf..96d6f0619733 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -182,7 +182,7 @@ void __init omapfb_reserve_sdram(void) return; bdata = NODE_DATA(0)->bdata; - sdram_start = bdata->node_boot_start; + sdram_start = bdata->node_min_pfn << PAGE_SHIFT; sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start; reserved = 0; for (i = 0; ; i++) { @@ -340,5 +340,3 @@ unsigned long omapfb_reserve_sram(unsigned long sram_pstart, #endif - - diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 786de88a82a7..3c85fdaa9487 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -119,8 +119,7 @@ void __init paging_init(void) unsigned long zones_size[MAX_NR_ZONES]; unsigned long low, start_pfn; - start_pfn = pgdat->bdata->node_boot_start; - start_pfn >>= PAGE_SHIFT; + start_pfn = pgdat->bdata->node_min_pfn; low = pgdat->bdata->node_low_pfn; memset(zones_size, 0, sizeof(zones_size)); diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 2fcf8464331e..d83125e1ed27 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -74,17 +74,17 @@ pg_data_t *pgdat_list[MAX_NUMNODES]; static int __init build_node_maps(unsigned long start, unsigned long len, int node) { - unsigned long cstart, epfn, end = start + len; + unsigned long spfn, epfn, end = start + len; struct bootmem_data *bdp = &bootmem_node_data[node]; epfn = GRANULEROUNDUP(end) >> PAGE_SHIFT; - cstart = GRANULEROUNDDOWN(start); + spfn = GRANULEROUNDDOWN(start) >> PAGE_SHIFT; if (!bdp->node_low_pfn) { - bdp->node_boot_start = cstart; + bdp->node_min_pfn = spfn; bdp->node_low_pfn = epfn; } else { - bdp->node_boot_start = min(cstart, bdp->node_boot_start); + bdp->node_min_pfn = min(spfn, bdp->node_min_pfn); bdp->node_low_pfn = max(epfn, bdp->node_low_pfn); } @@ -221,20 +221,21 @@ static void __init fill_pernode(int node, unsigned long pernode, static int __init find_pernode_space(unsigned long start, unsigned long len, int node) { - unsigned long epfn; + unsigned long spfn, epfn; unsigned long pernodesize = 0, pernode, pages, mapsize; struct bootmem_data *bdp = &bootmem_node_data[node]; + spfn = start >> PAGE_SHIFT; epfn = (start + len) >> PAGE_SHIFT; - pages = bdp->node_low_pfn - (bdp->node_boot_start >> PAGE_SHIFT); + pages = bdp->node_low_pfn - bdp->node_min_pfn; mapsize = bootmem_bootmap_pages(pages) << PAGE_SHIFT; /* * Make sure this memory falls within this node's usable memory * since we may have thrown some away in build_maps(). */ - if (start < bdp->node_boot_start || epfn > bdp->node_low_pfn) + if (spfn < bdp->node_min_pfn || epfn > bdp->node_low_pfn) return 0; /* Don't setup this node's local space twice... */ @@ -296,7 +297,7 @@ static void __init reserve_pernode_space(void) bdp = pdp->bdata; /* First the bootmem_map itself */ - pages = bdp->node_low_pfn - (bdp->node_boot_start>>PAGE_SHIFT); + pages = bdp->node_low_pfn - bdp->node_min_pfn; size = bootmem_bootmap_pages(pages) << PAGE_SHIFT; base = __pa(bdp->node_bootmem_map); reserve_bootmem_node(pdp, base, size, BOOTMEM_DEFAULT); @@ -466,7 +467,7 @@ void __init find_memory(void) init_bootmem_node(pgdat_list[node], map>>PAGE_SHIFT, - bdp->node_boot_start>>PAGE_SHIFT, + bdp->node_min_pfn, bdp->node_low_pfn); } diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c index cc23934bc41e..cbc3c4c54566 100644 --- a/arch/m32r/mm/discontig.c +++ b/arch/m32r/mm/discontig.c @@ -123,8 +123,7 @@ unsigned long __init setup_memory(void) return max_low_pfn; } -#define START_PFN(nid) \ - (NODE_DATA(nid)->bdata->node_boot_start >> PAGE_SHIFT) +#define START_PFN(nid) (NODE_DATA(nid)->bdata->node_min_pfn) #define MAX_LOW_PFN(nid) (NODE_DATA(nid)->bdata->node_low_pfn) unsigned long __init zone_sizes_init(void) diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index 28799af15e95..2554eb59cfef 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c @@ -93,8 +93,7 @@ void free_initrd_mem(unsigned long, unsigned long); #endif /* It'd be good if these lines were in the standard header file. */ -#define START_PFN(nid) \ - (NODE_DATA(nid)->bdata->node_boot_start >> PAGE_SHIFT) +#define START_PFN(nid) (NODE_DATA(nid)->bdata->node_min_pfn) #define MAX_LOW_PFN(nid) (NODE_DATA(nid)->bdata->node_low_pfn) #ifndef CONFIG_DISCONTIGMEM @@ -252,4 +251,3 @@ void free_initrd_mem(unsigned long start, unsigned long end) printk (KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10); } #endif - diff --git a/arch/mn10300/mm/init.c b/arch/mn10300/mm/init.c index 8c5d88c7b90a..8cee387a24fd 100644 --- a/arch/mn10300/mm/init.c +++ b/arch/mn10300/mm/init.c @@ -67,8 +67,8 @@ void __init paging_init(void) /* declare the sizes of the RAM zones (only use the normal zone) */ zones_size[ZONE_NORMAL] = - (contig_page_data.bdata->node_low_pfn) - - (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT); + contig_page_data.bdata->node_low_pfn - + contig_page_data.bdata->node_min_pfn; /* pass the memory from the bootmem allocator to the main allocator */ free_area_init(zones_size); @@ -87,7 +87,7 @@ void __init mem_init(void) if (!mem_map) BUG(); -#define START_PFN (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT) +#define START_PFN (contig_page_data.bdata->node_min_pfn) #define MAX_LOW_PFN (contig_page_data.bdata->node_low_pfn) max_mapnr = num_physpages = MAX_LOW_PFN - START_PFN; diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index d7df26bd1e54..d652d375eb1e 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -191,7 +191,7 @@ void __init paging_init(void) pg_data_t *pgdat = NODE_DATA(nid); unsigned long low, start_pfn; - start_pfn = pgdat->bdata->node_boot_start >> PAGE_SHIFT; + start_pfn = pgdat->bdata->node_min_pfn; low = pgdat->bdata->node_low_pfn; if (max_zone_pfns[ZONE_NORMAL] < low) diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 90921d10ffa2..4ddf2922fc8d 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -28,7 +28,7 @@ extern unsigned long saved_max_pfn; * memory pages (including holes) on the node. */ typedef struct bootmem_data { - unsigned long node_boot_start; + unsigned long node_min_pfn; unsigned long node_low_pfn; void *node_bootmem_map; unsigned long last_end_off; diff --git a/mm/bootmem.c b/mm/bootmem.c index 282b786c2b15..4af15d0340ad 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -80,7 +80,7 @@ static void __init link_bootmem(bootmem_data_t *bdata) bootmem_data_t *ent; ent = list_entry(iter, bootmem_data_t, list); - if (bdata->node_boot_start < ent->node_boot_start) + if (bdata->node_min_pfn < ent->node_min_pfn) break; } list_add_tail(&bdata->list, iter); @@ -96,7 +96,7 @@ static unsigned long __init init_bootmem_core(bootmem_data_t *bdata, mminit_validate_memmodel_limits(&start, &end); bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart)); - bdata->node_boot_start = PFN_PHYS(start); + bdata->node_min_pfn = start; bdata->node_low_pfn = end; link_bootmem(bdata); @@ -151,7 +151,7 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) if (!bdata->node_bootmem_map) return 0; - start = PFN_DOWN(bdata->node_boot_start); + start = bdata->node_min_pfn; end = bdata->node_low_pfn; /* @@ -167,7 +167,7 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) unsigned long *map, idx, vec; map = bdata->node_bootmem_map; - idx = start - PFN_DOWN(bdata->node_boot_start); + idx = start - bdata->node_min_pfn; vec = ~map[idx / BITS_PER_LONG]; if (aligned && vec == ~0UL && start + BITS_PER_LONG < end) { @@ -192,7 +192,7 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) } page = virt_to_page(bdata->node_bootmem_map); - pages = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); + pages = bdata->node_low_pfn - bdata->node_min_pfn; pages = bootmem_bootmap_pages(pages); count += pages; while (pages--) @@ -231,8 +231,8 @@ static void __init __free(bootmem_data_t *bdata, unsigned long idx; bdebug("nid=%td start=%lx end=%lx\n", bdata - bootmem_node_data, - sidx + PFN_DOWN(bdata->node_boot_start), - eidx + PFN_DOWN(bdata->node_boot_start)); + sidx + bdata->node_min_pfn, + eidx + bdata->node_min_pfn); if (bdata->hint_idx > sidx) bdata->hint_idx = sidx; @@ -250,8 +250,8 @@ static int __init __reserve(bootmem_data_t *bdata, unsigned long sidx, bdebug("nid=%td start=%lx end=%lx flags=%x\n", bdata - bootmem_node_data, - sidx + PFN_DOWN(bdata->node_boot_start), - eidx + PFN_DOWN(bdata->node_boot_start), + sidx + bdata->node_min_pfn, + eidx + bdata->node_min_pfn, flags); for (idx = sidx; idx < eidx; idx++) @@ -261,7 +261,7 @@ static int __init __reserve(bootmem_data_t *bdata, unsigned long sidx, return -EBUSY; } bdebug("silent double reserve of PFN %lx\n", - idx + PFN_DOWN(bdata->node_boot_start)); + idx + bdata->node_min_pfn); } return 0; } @@ -275,11 +275,11 @@ static int __init mark_bootmem_node(bootmem_data_t *bdata, bdebug("nid=%td start=%lx end=%lx reserve=%d flags=%x\n", bdata - bootmem_node_data, start, end, reserve, flags); - BUG_ON(start < PFN_DOWN(bdata->node_boot_start)); + BUG_ON(start < bdata->node_min_pfn); BUG_ON(end > bdata->node_low_pfn); - sidx = start - PFN_DOWN(bdata->node_boot_start); - eidx = end - PFN_DOWN(bdata->node_boot_start); + sidx = start - bdata->node_min_pfn; + eidx = end - bdata->node_min_pfn; if (reserve) return __reserve(bdata, sidx, eidx, flags); @@ -299,7 +299,8 @@ static int __init mark_bootmem(unsigned long start, unsigned long end, int err; unsigned long max; - if (pos < PFN_DOWN(bdata->node_boot_start)) { + if (pos < bdata->node_min_pfn || + pos >= bdata->node_low_pfn) { BUG_ON(pos != start); continue; } @@ -422,7 +423,7 @@ static void * __init alloc_bootmem_core(struct bootmem_data *bdata, bdata - bootmem_node_data, size, PAGE_ALIGN(size) >> PAGE_SHIFT, align, goal, limit); - min = PFN_DOWN(bdata->node_boot_start); + min = bdata->node_min_pfn; max = bdata->node_low_pfn; goal >>= PAGE_SHIFT; @@ -440,8 +441,8 @@ static void * __init alloc_bootmem_core(struct bootmem_data *bdata, else start = ALIGN(min, step); - sidx = start - PFN_DOWN(bdata->node_boot_start); - midx = max - PFN_DOWN(bdata->node_boot_start); + sidx = start - bdata->node_min_pfn;; + midx = max - bdata->node_min_pfn; if (bdata->hint_idx > sidx) { /* @@ -491,7 +492,8 @@ find_block: PFN_UP(end_off), BOOTMEM_EXCLUSIVE)) BUG(); - region = phys_to_virt(bdata->node_boot_start + start_off); + region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) + + start_off); memset(region, 0, size); return region; } @@ -518,7 +520,7 @@ restart: if (goal && bdata->node_low_pfn <= PFN_DOWN(goal)) continue; - if (limit && bdata->node_boot_start >= limit) + if (limit && bdata->node_min_pfn >= PFN_DOWN(limit)) break; region = alloc_bootmem_core(bdata, size, align, goal, limit); -- cgit From 2be0ffe2b29bd31d3debd0877797892ff2d91f4c Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 23 Jul 2008 21:28:11 -0700 Subject: mm: add alloc_pages_exact() and free_pages_exact() alloc_pages_exact() is similar to alloc_pages(), except that it allocates the minimum number of pages to fulfill the request. This is useful if you want to allocate a very large buffer that is slightly larger than an even power-of-two number of pages. In that case, alloc_pages() will waste a lot of memory. I have a video driver that wants to allocate a 5MB buffer. alloc_pages() wiill waste 3MB of physically-contiguous memory. Signed-off-by: Timur Tabi Cc: Andi Kleen Acked-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 3 +++ mm/page_alloc.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) (limited to 'include/linux') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index f640ed241422..e8003afeffba 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -228,6 +228,9 @@ extern struct page *alloc_page_vma(gfp_t gfp_mask, extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order); extern unsigned long get_zeroed_page(gfp_t gfp_mask); +void *alloc_pages_exact(size_t size, gfp_t gfp_mask); +void free_pages_exact(void *virt, size_t size); + #define __get_free_page(gfp_mask) \ __get_free_pages((gfp_mask),0) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index eaa86671ebbd..8d528d57b403 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1697,6 +1697,59 @@ void free_pages(unsigned long addr, unsigned int order) EXPORT_SYMBOL(free_pages); +/** + * alloc_pages_exact - allocate an exact number physically-contiguous pages. + * @size: the number of bytes to allocate + * @gfp_mask: GFP flags for the allocation + * + * This function is similar to alloc_pages(), except that it allocates the + * minimum number of pages to satisfy the request. alloc_pages() can only + * allocate memory in power-of-two pages. + * + * This function is also limited by MAX_ORDER. + * + * Memory allocated by this function must be released by free_pages_exact(). + */ +void *alloc_pages_exact(size_t size, gfp_t gfp_mask) +{ + unsigned int order = get_order(size); + unsigned long addr; + + addr = __get_free_pages(gfp_mask, order); + if (addr) { + unsigned long alloc_end = addr + (PAGE_SIZE << order); + unsigned long used = addr + PAGE_ALIGN(size); + + split_page(virt_to_page(addr), order); + while (used < alloc_end) { + free_page(used); + used += PAGE_SIZE; + } + } + + return (void *)addr; +} +EXPORT_SYMBOL(alloc_pages_exact); + +/** + * free_pages_exact - release memory allocated via alloc_pages_exact() + * @virt: the value returned by alloc_pages_exact. + * @size: size of allocation, same value as passed to alloc_pages_exact(). + * + * Release the memory allocated by a previous call to alloc_pages_exact. + */ +void free_pages_exact(void *virt, size_t size) +{ + unsigned long addr = (unsigned long)virt; + unsigned long end = addr + PAGE_ALIGN(size); + + while (addr < end) { + free_page(addr); + addr += PAGE_SIZE; + } +} +EXPORT_SYMBOL(free_pages_exact); + static unsigned int nr_free_zone_pages(int offset) { struct zoneref *z; -- cgit From 27ac792ca0b0a1e7e65f20342260650516c95864 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Wed, 23 Jul 2008 21:28:13 -0700 Subject: PAGE_ALIGN(): correctly handle 64-bit values on 32-bit architectures On 32-bit architectures PAGE_ALIGN() truncates 64-bit values to the 32-bit boundary. For example: u64 val = PAGE_ALIGN(size); always returns a value < 4GB even if size is greater than 4GB. The problem resides in PAGE_MASK definition (from include/asm-x86/page.h for example): #define PAGE_SHIFT 12 #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) ... #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) The "~" is performed on a 32-bit value, so everything in "and" with PAGE_MASK greater than 4GB will be truncated to the 32-bit boundary. Using the ALIGN() macro seems to be the right way, because it uses typeof(addr) for the mask. Also move the PAGE_ALIGN() definitions out of include/asm-*/page.h in include/linux/mm.h. See also lkml discussion: http://lkml.org/lkml/2008/6/11/237 [akpm@linux-foundation.org: fix drivers/media/video/uvc/uvc_queue.c] [akpm@linux-foundation.org: fix v850] [akpm@linux-foundation.org: fix powerpc] [akpm@linux-foundation.org: fix arm] [akpm@linux-foundation.org: fix mips] [akpm@linux-foundation.org: fix drivers/media/video/pvrusb2/pvrusb2-dvb.c] [akpm@linux-foundation.org: fix drivers/mtd/maps/uclinux.c] [akpm@linux-foundation.org: fix powerpc] Signed-off-by: Andrea Righi Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/kernel/module.c | 1 + arch/arm/plat-omap/fb.c | 1 + arch/avr32/mm/ioremap.c | 1 + arch/h8300/kernel/setup.c | 1 + arch/m68k/amiga/chipram.c | 1 + arch/m68knommu/kernel/setup.c | 1 + arch/mips/kernel/module.c | 1 + arch/mips/sgi-ip27/ip27-klnuma.c | 1 + arch/powerpc/kernel/suspend.c | 1 + arch/powerpc/lib/code-patching.c | 1 + arch/sparc64/kernel/iommu_common.h | 2 +- arch/x86/kernel/module_64.c | 1 + arch/xtensa/kernel/setup.c | 1 + drivers/char/random.c | 1 + drivers/ieee1394/iso.c | 1 + drivers/media/video/pvrusb2/pvrusb2-dvb.c | 1 + drivers/media/video/pvrusb2/pvrusb2-ioread.c | 1 + drivers/media/video/uvc/uvc_queue.c | 1 + drivers/media/video/videobuf-core.c | 1 + drivers/mtd/maps/uclinux.c | 1 + drivers/net/mlx4/eq.c | 1 + drivers/pcmcia/electra_cf.c | 1 + drivers/scsi/sun_esp.c | 1 + drivers/video/acornfb.c | 1 + drivers/video/imxfb.c | 1 + drivers/video/omap/dispc.c | 1 + drivers/video/omap/omapfb_main.c | 1 + drivers/video/pxafb.c | 1 + drivers/video/sa1100fb.c | 1 + include/asm-alpha/page.h | 3 --- include/asm-arm/page-nommu.h | 4 +--- include/asm-arm/page.h | 3 --- include/asm-avr32/page.h | 3 --- include/asm-blackfin/page.h | 3 --- include/asm-cris/page.h | 3 --- include/asm-frv/page.h | 3 --- include/asm-h8300/page.h | 3 --- include/asm-ia64/page.h | 1 - include/asm-m32r/page.h | 3 --- include/asm-m68k/dvma.h | 2 +- include/asm-m68k/page.h | 3 --- include/asm-m68knommu/page.h | 3 --- include/asm-mips/page.h | 3 --- include/asm-mips/processor.h | 2 +- include/asm-mn10300/page.h | 3 --- include/asm-parisc/page.h | 4 ---- include/asm-powerpc/page.h | 3 --- include/asm-s390/page.h | 3 --- include/asm-sh/page.h | 3 --- include/asm-sparc/page_32.h | 3 --- include/asm-sparc/page_64.h | 3 --- include/asm-um/page.h | 3 --- include/asm-v850/page.h | 4 ---- include/asm-x86/page.h | 3 --- include/asm-xtensa/page.h | 2 -- include/linux/mm.h | 3 +++ sound/core/info.c | 1 + 57 files changed, 36 insertions(+), 74 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index 79b7e5cf5416..a68259a0cccd 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index 96d6f0619733..5d107520e6b9 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include diff --git a/arch/avr32/mm/ioremap.c b/arch/avr32/mm/ioremap.c index 3437c82434ac..f03b79f0e0ab 100644 --- a/arch/avr32/mm/ioremap.c +++ b/arch/avr32/mm/ioremap.c @@ -6,6 +6,7 @@ * published by the Free Software Foundation. */ #include +#include #include #include diff --git a/arch/h8300/kernel/setup.c b/arch/h8300/kernel/setup.c index b1f25c20a5db..7fda657110eb 100644 --- a/arch/h8300/kernel/setup.c +++ b/arch/h8300/kernel/setup.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/m68k/amiga/chipram.c b/arch/m68k/amiga/chipram.c index cbe36538af47..61df1d33c050 100644 --- a/arch/m68k/amiga/chipram.c +++ b/arch/m68k/amiga/chipram.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/arch/m68knommu/kernel/setup.c b/arch/m68knommu/kernel/setup.c index 03f4fe6a2fc0..5985f1989021 100644 --- a/arch/m68knommu/kernel/setup.c +++ b/arch/m68knommu/kernel/setup.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c index e7ed0ac48537..1f60e27523d9 100644 --- a/arch/mips/kernel/module.c +++ b/arch/mips/kernel/module.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include diff --git a/arch/mips/sgi-ip27/ip27-klnuma.c b/arch/mips/sgi-ip27/ip27-klnuma.c index 48932ce1d730..d9c79d8be81d 100644 --- a/arch/mips/sgi-ip27/ip27-klnuma.c +++ b/arch/mips/sgi-ip27/ip27-klnuma.c @@ -4,6 +4,7 @@ * Copyright 2000 - 2001 Kanoj Sarcar (kanoj@sgi.com) */ #include +#include #include #include #include diff --git a/arch/powerpc/kernel/suspend.c b/arch/powerpc/kernel/suspend.c index 8cee57107541..6fc6328dc626 100644 --- a/arch/powerpc/kernel/suspend.c +++ b/arch/powerpc/kernel/suspend.c @@ -7,6 +7,7 @@ * Copyright (c) 2001 Patrick Mochel */ +#include #include /* References to section boundaries */ diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c index 0559fe086eb4..7c975d43e3f3 100644 --- a/arch/powerpc/lib/code-patching.c +++ b/arch/powerpc/lib/code-patching.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include diff --git a/arch/sparc64/kernel/iommu_common.h b/arch/sparc64/kernel/iommu_common.h index f3575a614fa2..53b19c8231a9 100644 --- a/arch/sparc64/kernel/iommu_common.h +++ b/arch/sparc64/kernel/iommu_common.h @@ -23,7 +23,7 @@ #define IO_PAGE_SHIFT 13 #define IO_PAGE_SIZE (1UL << IO_PAGE_SHIFT) #define IO_PAGE_MASK (~(IO_PAGE_SIZE-1)) -#define IO_PAGE_ALIGN(addr) (((addr)+IO_PAGE_SIZE-1)&IO_PAGE_MASK) +#define IO_PAGE_ALIGN(addr) ALIGN(addr, IO_PAGE_SIZE) #define IO_TSB_ENTRIES (128*1024) #define IO_TSB_SIZE (IO_TSB_ENTRIES * 8) diff --git a/arch/x86/kernel/module_64.c b/arch/x86/kernel/module_64.c index 0e867676b5a5..6ba87830d4b1 100644 --- a/arch/x86/kernel/module_64.c +++ b/arch/x86/kernel/module_64.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 5e6d75c9f92b..a00359e8f7a8 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include diff --git a/drivers/char/random.c b/drivers/char/random.c index 0cf98bd4f2d2..e0d0e371909c 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -236,6 +236,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c index 07ca35c98f96..1cf6487b65ba 100644 --- a/drivers/ieee1394/iso.c +++ b/drivers/ieee1394/iso.c @@ -11,6 +11,7 @@ #include #include +#include #include #include "hosts.h" diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c index 6ec4bf81fc7f..77b3c3385066 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c +++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c @@ -20,6 +20,7 @@ #include #include +#include #include "dvbdev.h" #include "pvrusb2-debug.h" #include "pvrusb2-hdw-internal.h" diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c index 05a1376405e7..b4824782d858 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -22,6 +22,7 @@ #include "pvrusb2-debug.h" #include #include +#include #include #include #include diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 7388d0cee3d4..5646a6a32939 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 0a88c44ace00..b7b05842cf28 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index c42f4b83f686..3fcf92130aa4 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c index e141a1513f07..ea3a09aaa844 100644 --- a/drivers/net/mlx4/eq.c +++ b/drivers/net/mlx4/eq.c @@ -33,6 +33,7 @@ #include #include +#include #include #include diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c index c21f9a9c3e3f..a34284b1482a 100644 --- a/drivers/pcmcia/electra_cf.c +++ b/drivers/pcmcia/electra_cf.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c index 2c87db98cdfb..f9cf70151366 100644 --- a/drivers/scsi/sun_esp.c +++ b/drivers/scsi/sun_esp.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c index eedb8285e32f..017233d0c481 100644 --- a/drivers/video/acornfb.c +++ b/drivers/video/acornfb.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 94e4d3ac1a05..0c5a475c1cae 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index ab32ceb06178..ab77c51fe9d6 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -20,6 +20,7 @@ */ #include #include +#include #include #include #include diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c index 14d0f7a11145..f85af5c4fa68 100644 --- a/drivers/video/omap/omapfb_main.c +++ b/drivers/video/omap/omapfb_main.c @@ -25,6 +25,7 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include +#include #include #include diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index bb2514369507..5e8a140399fc 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c index ab2b2110478b..4a9f7e121807 100644 --- a/drivers/video/sa1100fb.c +++ b/drivers/video/sa1100fb.c @@ -167,6 +167,7 @@ #include #include #include +#include #include #include #include diff --git a/include/asm-alpha/page.h b/include/asm-alpha/page.h index 22ff9762d17b..0995f9d13417 100644 --- a/include/asm-alpha/page.h +++ b/include/asm-alpha/page.h @@ -80,9 +80,6 @@ typedef struct page *pgtable_t; #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) #define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET)) #ifndef CONFIG_DISCONTIGMEM diff --git a/include/asm-arm/page-nommu.h b/include/asm-arm/page-nommu.h index a1bcad060480..ea1cde84f500 100644 --- a/include/asm-arm/page-nommu.h +++ b/include/asm-arm/page-nommu.h @@ -7,6 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + #ifndef _ASMARM_PAGE_NOMMU_H #define _ASMARM_PAGE_NOMMU_H @@ -42,9 +43,6 @@ typedef unsigned long pgprot_t; #define __pmd(x) (x) #define __pgprot(x) (x) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - extern unsigned long memory_start; extern unsigned long memory_end; diff --git a/include/asm-arm/page.h b/include/asm-arm/page.h index 8e05bdb5f12f..7c5fc5582e5d 100644 --- a/include/asm-arm/page.h +++ b/include/asm-arm/page.h @@ -15,9 +15,6 @@ #define PAGE_SIZE (1UL << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #ifndef __ASSEMBLY__ #ifndef CONFIG_MMU diff --git a/include/asm-avr32/page.h b/include/asm-avr32/page.h index cbbc5ca9728b..f805d1cb11bc 100644 --- a/include/asm-avr32/page.h +++ b/include/asm-avr32/page.h @@ -57,9 +57,6 @@ static inline int get_order(unsigned long size) #endif /* !__ASSEMBLY__ */ -/* Align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - /* * The hardware maps the virtual addresses 0x80000000 -> 0x9fffffff * permanently to the physical addresses 0x00000000 -> 0x1fffffff when diff --git a/include/asm-blackfin/page.h b/include/asm-blackfin/page.h index c7db0220fbd6..344f6a8c1f22 100644 --- a/include/asm-blackfin/page.h +++ b/include/asm-blackfin/page.h @@ -51,9 +51,6 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - extern unsigned long memory_start; extern unsigned long memory_end; diff --git a/include/asm-cris/page.h b/include/asm-cris/page.h index c45bb1ef397c..d19272ba6b69 100644 --- a/include/asm-cris/page.h +++ b/include/asm-cris/page.h @@ -60,9 +60,6 @@ typedef struct page *pgtable_t; #define page_to_phys(page) __pa((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #ifndef __ASSEMBLY__ #endif /* __ASSEMBLY__ */ diff --git a/include/asm-frv/page.h b/include/asm-frv/page.h index c2c1e89e747d..bd9c220094c7 100644 --- a/include/asm-frv/page.h +++ b/include/asm-frv/page.h @@ -40,9 +40,6 @@ typedef struct page *pgtable_t; #define __pgprot(x) ((pgprot_t) { (x) } ) #define PTE_MASK PAGE_MASK -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - #define devmem_is_allowed(pfn) 1 #define __pa(vaddr) virt_to_phys((void *) (unsigned long) (vaddr)) diff --git a/include/asm-h8300/page.h b/include/asm-h8300/page.h index d6a3eaf3b27e..0b6acf0b03aa 100644 --- a/include/asm-h8300/page.h +++ b/include/asm-h8300/page.h @@ -43,9 +43,6 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - extern unsigned long memory_start; extern unsigned long memory_end; diff --git a/include/asm-ia64/page.h b/include/asm-ia64/page.h index 36f39321b768..5f271bc712ee 100644 --- a/include/asm-ia64/page.h +++ b/include/asm-ia64/page.h @@ -40,7 +40,6 @@ #define PAGE_SIZE (__IA64_UL_CONST(1) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE - 1)) -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) #define PERCPU_PAGE_SHIFT 16 /* log2() of max. size of per-CPU area */ #define PERCPU_PAGE_SIZE (__IA64_UL_CONST(1) << PERCPU_PAGE_SHIFT) diff --git a/include/asm-m32r/page.h b/include/asm-m32r/page.h index 8a677f3fca68..c9333089fe11 100644 --- a/include/asm-m32r/page.h +++ b/include/asm-m32r/page.h @@ -41,9 +41,6 @@ typedef struct page *pgtable_t; #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - /* * This handles the memory map.. We could make this a config * option, but too many people screw it up, and too few need diff --git a/include/asm-m68k/dvma.h b/include/asm-m68k/dvma.h index 4fff408d0150..890bbf7e7758 100644 --- a/include/asm-m68k/dvma.h +++ b/include/asm-m68k/dvma.h @@ -13,7 +13,7 @@ #define DVMA_PAGE_SHIFT 13 #define DVMA_PAGE_SIZE (1UL << DVMA_PAGE_SHIFT) #define DVMA_PAGE_MASK (~(DVMA_PAGE_SIZE-1)) -#define DVMA_PAGE_ALIGN(addr) (((addr)+DVMA_PAGE_SIZE-1)&DVMA_PAGE_MASK) +#define DVMA_PAGE_ALIGN(addr) ALIGN(addr, DVMA_PAGE_SIZE) extern void dvma_init(void); extern int dvma_map_iommu(unsigned long kaddr, unsigned long baddr, diff --git a/include/asm-m68k/page.h b/include/asm-m68k/page.h index 880c2cbff8a6..a34b8bad7847 100644 --- a/include/asm-m68k/page.h +++ b/include/asm-m68k/page.h @@ -103,9 +103,6 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #endif /* !__ASSEMBLY__ */ #include diff --git a/include/asm-m68knommu/page.h b/include/asm-m68knommu/page.h index 1e82ebb7d644..3a1ede4544cb 100644 --- a/include/asm-m68knommu/page.h +++ b/include/asm-m68knommu/page.h @@ -43,9 +43,6 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - extern unsigned long memory_start; extern unsigned long memory_end; diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index 494f00ba9541..fe7a88ea066e 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -137,9 +137,6 @@ typedef struct { unsigned long pgprot; } pgprot_t; #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - /* * __pa()/__va() should be used only during mem init. */ diff --git a/include/asm-mips/processor.h b/include/asm-mips/processor.h index 58cbac5a64e4..a1e4453469f9 100644 --- a/include/asm-mips/processor.h +++ b/include/asm-mips/processor.h @@ -45,7 +45,7 @@ extern unsigned int vced_count, vcei_count; * This decides where the kernel will search for a free chunk of vm * space during mmap's. */ -#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3)) +#define TASK_UNMAPPED_BASE ((TASK_SIZE / 3) & ~(PAGE_SIZE)) #endif #ifdef CONFIG_64BIT diff --git a/include/asm-mn10300/page.h b/include/asm-mn10300/page.h index 124971b9fb9b..8288e124165b 100644 --- a/include/asm-mn10300/page.h +++ b/include/asm-mn10300/page.h @@ -61,9 +61,6 @@ typedef struct page *pgtable_t; #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - /* * This handles the memory map.. We could make this a config * option, but too many people screw it up, and too few need diff --git a/include/asm-parisc/page.h b/include/asm-parisc/page.h index 27d50b859541..c3941f09a878 100644 --- a/include/asm-parisc/page.h +++ b/include/asm-parisc/page.h @@ -119,10 +119,6 @@ extern int npmem_ranges; #define PMD_ENTRY_SIZE (1UL << BITS_PER_PMD_ENTRY) #define PTE_ENTRY_SIZE (1UL << BITS_PER_PTE_ENTRY) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - - #define LINUX_GATEWAY_SPACE 0 /* This governs the relationship between virtual and physical addresses. diff --git a/include/asm-powerpc/page.h b/include/asm-powerpc/page.h index cffdf0eb0df6..e088545cb3f5 100644 --- a/include/asm-powerpc/page.h +++ b/include/asm-powerpc/page.h @@ -119,9 +119,6 @@ extern phys_addr_t kernstart_addr; /* align addr on a size boundary - adjust address up if needed */ #define _ALIGN(addr,size) _ALIGN_UP(addr,size) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) _ALIGN(addr, PAGE_SIZE) - /* * Don't compare things with KERNELBASE or PAGE_OFFSET to test for * "kernelness", use is_kernel_addr() - it should do what you want. diff --git a/include/asm-s390/page.h b/include/asm-s390/page.h index 12fd9c4f0f15..991ba939408c 100644 --- a/include/asm-s390/page.h +++ b/include/asm-s390/page.h @@ -138,9 +138,6 @@ void arch_alloc_page(struct page *page, int order); #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #define __PAGE_OFFSET 0x0UL #define PAGE_OFFSET 0x0UL #define __pa(x) (unsigned long)(x) diff --git a/include/asm-sh/page.h b/include/asm-sh/page.h index 304c30b5d947..5dc01d2fcc4c 100644 --- a/include/asm-sh/page.h +++ b/include/asm-sh/page.h @@ -22,9 +22,6 @@ #define PAGE_MASK (~(PAGE_SIZE-1)) #define PTE_MASK PAGE_MASK -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #if defined(CONFIG_HUGETLB_PAGE_SIZE_64K) #define HPAGE_SHIFT 16 #elif defined(CONFIG_HUGETLB_PAGE_SIZE_256K) diff --git a/include/asm-sparc/page_32.h b/include/asm-sparc/page_32.h index 14de518cc38f..cf5fb70ca1c1 100644 --- a/include/asm-sparc/page_32.h +++ b/include/asm-sparc/page_32.h @@ -134,9 +134,6 @@ BTFIXUPDEF_SETHI(sparc_unmapped_base) #endif /* !(__ASSEMBLY__) */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #define PAGE_OFFSET 0xf0000000 #ifndef __ASSEMBLY__ extern unsigned long phys_base; diff --git a/include/asm-sparc/page_64.h b/include/asm-sparc/page_64.h index a8a2bba032c1..b579b910ef51 100644 --- a/include/asm-sparc/page_64.h +++ b/include/asm-sparc/page_64.h @@ -106,9 +106,6 @@ typedef struct page *pgtable_t; #endif /* !(__ASSEMBLY__) */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - /* We used to stick this into a hard-coded global register (%g4) * but that does not make sense anymore. */ diff --git a/include/asm-um/page.h b/include/asm-um/page.h index 916e1a61999f..335c57383c02 100644 --- a/include/asm-um/page.h +++ b/include/asm-um/page.h @@ -92,9 +92,6 @@ typedef struct page *pgtable_t; #define __pgd(x) ((pgd_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } ) -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - extern unsigned long uml_physmem; #define PAGE_OFFSET (uml_physmem) diff --git a/include/asm-v850/page.h b/include/asm-v850/page.h index 74a539a9bd59..f9de35d873fa 100644 --- a/include/asm-v850/page.h +++ b/include/asm-v850/page.h @@ -94,10 +94,6 @@ typedef unsigned long pgprot_t; #endif /* !__ASSEMBLY__ */ -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) - - /* No current v850 processor has virtual memory. */ #define __virt_to_phys(addr) (addr) #define __phys_to_virt(addr) (addr) diff --git a/include/asm-x86/page.h b/include/asm-x86/page.h index 6e02098b1605..49982110e4d9 100644 --- a/include/asm-x86/page.h +++ b/include/asm-x86/page.h @@ -34,9 +34,6 @@ #define HUGE_MAX_HSTATE 2 -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - #ifndef __ASSEMBLY__ #include #endif diff --git a/include/asm-xtensa/page.h b/include/asm-xtensa/page.h index 80a6ae0dd259..11f7dc2dbec7 100644 --- a/include/asm-xtensa/page.h +++ b/include/asm-xtensa/page.h @@ -26,13 +26,11 @@ /* * PAGE_SHIFT determines the page size - * PAGE_ALIGN(x) aligns the pointer to the (next) page boundary */ #define PAGE_SHIFT 12 #define PAGE_SIZE (__XTENSA_UL_CONST(1) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) -#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE - 1) & PAGE_MASK) #define PAGE_OFFSET XCHAL_KSEG_CACHED_VADDR #define MAX_MEM_PFN XCHAL_KSEG_SIZE diff --git a/include/linux/mm.h b/include/linux/mm.h index df322fb4df31..d87a5a5fe87d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -41,6 +41,9 @@ extern unsigned long mmap_min_addr; #define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n)) +/* to align the pointer to the (next) page boundary */ +#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE) + /* * Linux kernel virtual memory manager primitives. * The idea being to have a "virtual" mm in the same way diff --git a/sound/core/info.c b/sound/core/info.c index cb5ead3e202d..c67773ad9298 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include -- cgit From af370fb8cb3031f20438f246798d5f0d98089f29 Mon Sep 17 00:00:00 2001 From: Yasunori Goto Date: Wed, 23 Jul 2008 21:28:17 -0700 Subject: memory hotplug: small fixes to bootmem freeing for memory hotremove - Change some naming * Magic -> types * MIX_INFO -> MIX_SECTION_INFO * Change definition of bootmem type from direct hex value - __free_pages_bootmem() becomes __meminit. Signed-off-by: Yasunori Goto Cc: Andy Whitcroft Cc: Badari Pulavarty Cc: Yinghai Lu Cc: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memory_hotplug.h | 8 ++++---- mm/memory_hotplug.c | 12 ++++++------ mm/page_alloc.c | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index ea9f5ad9ec8e..3628e5088f64 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -13,12 +13,12 @@ struct mem_section; #ifdef CONFIG_MEMORY_HOTPLUG /* - * Magic number for free bootmem. + * Types for free bootmem. * The normal smallest mapcount is -1. Here is smaller value than it. */ -#define SECTION_INFO 0xfffffffe -#define MIX_INFO 0xfffffffd -#define NODE_INFO 0xfffffffc +#define SECTION_INFO (-1 - 1) +#define MIX_SECTION_INFO (-1 - 2) +#define NODE_INFO (-1 - 3) /* * pgdat resizing functions diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index ec85c37dcfb9..0fb05b258f0c 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -62,9 +62,9 @@ static void release_memory_resource(struct resource *res) #ifdef CONFIG_MEMORY_HOTPLUG_SPARSE #ifndef CONFIG_SPARSEMEM_VMEMMAP -static void get_page_bootmem(unsigned long info, struct page *page, int magic) +static void get_page_bootmem(unsigned long info, struct page *page, int type) { - atomic_set(&page->_mapcount, magic); + atomic_set(&page->_mapcount, type); SetPagePrivate(page); set_page_private(page, info); atomic_inc(&page->_count); @@ -72,10 +72,10 @@ static void get_page_bootmem(unsigned long info, struct page *page, int magic) void put_page_bootmem(struct page *page) { - int magic; + int type; - magic = atomic_read(&page->_mapcount); - BUG_ON(magic >= -1); + type = atomic_read(&page->_mapcount); + BUG_ON(type >= -1); if (atomic_dec_return(&page->_count) == 1) { ClearPagePrivate(page); @@ -119,7 +119,7 @@ static void register_page_bootmem_info_section(unsigned long start_pfn) mapsize = PAGE_ALIGN(usemap_size()) >> PAGE_SHIFT; for (i = 0; i < mapsize; i++, page++) - get_page_bootmem(section_nr, page, MIX_INFO); + get_page_bootmem(section_nr, page, MIX_SECTION_INFO); } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index cd4c41432ef6..6da667274df5 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -533,7 +533,7 @@ static void __free_pages_ok(struct page *page, unsigned int order) /* * permit the bootmem allocator to evade page validation on high-order frees */ -void __free_pages_bootmem(struct page *page, unsigned int order) +void __meminit __free_pages_bootmem(struct page *page, unsigned int order) { if (order == 0) { __ClearPageReserved(page); -- cgit From 5c755e9fd813810680abd56ec09a5f90143e815b Mon Sep 17 00:00:00 2001 From: Badari Pulavarty Date: Wed, 23 Jul 2008 21:28:19 -0700 Subject: memory-hotplug: add sysfs removable attribute for hotplug memory remove Memory may be hot-removed on a per-memory-block basis, particularly on POWER where the SPARSEMEM section size often matches the memory-block size. A user-level agent must be able to identify which sections of memory are likely to be removable before attempting the potentially expensive operation. This patch adds a file called "removable" to the memory directory in sysfs to help such an agent. In this patch, a memory block is considered removable if; o It contains only MOVABLE pageblocks o It contains only pageblocks with free pages regardless of pageblock type On the other hand, a memory block starting with a PageReserved() page will never be considered removable. Without this patch, the user-agent is forced to choose a memory block to remove randomly. Sample output of the sysfs files: ./memory/memory0/removable: 0 ./memory/memory1/removable: 0 ./memory/memory2/removable: 0 ./memory/memory3/removable: 0 ./memory/memory4/removable: 0 ./memory/memory5/removable: 0 ./memory/memory6/removable: 0 ./memory/memory7/removable: 1 ./memory/memory8/removable: 0 ./memory/memory9/removable: 0 ./memory/memory10/removable: 0 ./memory/memory11/removable: 0 ./memory/memory12/removable: 0 ./memory/memory13/removable: 0 ./memory/memory14/removable: 0 ./memory/memory15/removable: 0 ./memory/memory16/removable: 0 ./memory/memory17/removable: 1 ./memory/memory18/removable: 1 ./memory/memory19/removable: 1 ./memory/memory20/removable: 1 ./memory/memory21/removable: 1 ./memory/memory22/removable: 1 Signed-off-by: Badari Pulavarty Signed-off-by: Mel Gorman Acked-by: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ABI/testing/sysfs-devices-memory | 24 +++++++++++ drivers/base/memory.c | 19 ++++++++ include/linux/memory_hotplug.h | 12 ++++++ mm/memory_hotplug.c | 60 ++++++++++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-devices-memory (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-devices-memory b/Documentation/ABI/testing/sysfs-devices-memory new file mode 100644 index 000000000000..7a16fe1e2270 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-memory @@ -0,0 +1,24 @@ +What: /sys/devices/system/memory +Date: June 2008 +Contact: Badari Pulavarty +Description: + The /sys/devices/system/memory contains a snapshot of the + internal state of the kernel memory blocks. Files could be + added or removed dynamically to represent hot-add/remove + operations. + +Users: hotplug memory add/remove tools + https://w3.opensource.ibm.com/projects/powerpc-utils/ + +What: /sys/devices/system/memory/memoryX/removable +Date: June 2008 +Contact: Badari Pulavarty +Description: + The file /sys/devices/system/memory/memoryX/removable + indicates whether this memory block is removable or not. + This is useful for a user-level agent to determine + identify removable sections of the memory before attempting + potentially expensive hot-remove memory operation + +Users: hotplug memory remove tools + https://w3.opensource.ibm.com/projects/powerpc-utils/ diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 4d4e0e7b6e92..855ed1a9f97b 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -100,6 +100,21 @@ static ssize_t show_mem_phys_index(struct sys_device *dev, return sprintf(buf, "%08lx\n", mem->phys_index); } +/* + * Show whether the section of memory is likely to be hot-removable + */ +static ssize_t show_mem_removable(struct sys_device *dev, char *buf) +{ + unsigned long start_pfn; + int ret; + struct memory_block *mem = + container_of(dev, struct memory_block, sysdev); + + start_pfn = section_nr_to_pfn(mem->phys_index); + ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION); + return sprintf(buf, "%d\n", ret); +} + /* * online, offline, going offline, etc. */ @@ -262,6 +277,7 @@ static ssize_t show_phys_device(struct sys_device *dev, static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL); static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state); static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL); +static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL); #define mem_create_simple_file(mem, attr_name) \ sysdev_create_file(&mem->sysdev, &attr_##attr_name) @@ -350,6 +366,8 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section, ret = mem_create_simple_file(mem, state); if (!ret) ret = mem_create_simple_file(mem, phys_device); + if (!ret) + ret = mem_create_simple_file(mem, removable); return ret; } @@ -394,6 +412,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, mem_remove_simple_file(mem, phys_index); mem_remove_simple_file(mem, state); mem_remove_simple_file(mem, phys_device); + mem_remove_simple_file(mem, removable); unregister_memory(mem, section); return 0; diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 3628e5088f64..763ba81fc0f0 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -199,6 +199,18 @@ extern int walk_memory_resource(unsigned long start_pfn, unsigned long nr_pages, void *arg, int (*func)(unsigned long, unsigned long, void *)); +#ifdef CONFIG_MEMORY_HOTREMOVE + +extern int is_mem_section_removable(unsigned long pfn, unsigned long nr_pages); + +#else +static inline int is_mem_section_removable(unsigned long pfn, + unsigned long nr_pages) +{ + return 0; +} +#endif /* CONFIG_MEMORY_HOTREMOVE */ + extern int add_memory(int nid, u64 start, u64 size); extern int arch_add_memory(int nid, u64 start, u64 size); extern int remove_memory(u64 start, u64 size); diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 93aba78dc8b6..89fee2dcb039 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -522,6 +522,66 @@ error: EXPORT_SYMBOL_GPL(add_memory); #ifdef CONFIG_MEMORY_HOTREMOVE +/* + * A free page on the buddy free lists (not the per-cpu lists) has PageBuddy + * set and the size of the free page is given by page_order(). Using this, + * the function determines if the pageblock contains only free pages. + * Due to buddy contraints, a free page at least the size of a pageblock will + * be located at the start of the pageblock + */ +static inline int pageblock_free(struct page *page) +{ + return PageBuddy(page) && page_order(page) >= pageblock_order; +} + +/* Return the start of the next active pageblock after a given page */ +static struct page *next_active_pageblock(struct page *page) +{ + int pageblocks_stride; + + /* Ensure the starting page is pageblock-aligned */ + BUG_ON(page_to_pfn(page) & (pageblock_nr_pages - 1)); + + /* Move forward by at least 1 * pageblock_nr_pages */ + pageblocks_stride = 1; + + /* If the entire pageblock is free, move to the end of free page */ + if (pageblock_free(page)) + pageblocks_stride += page_order(page) - pageblock_order; + + return page + (pageblocks_stride * pageblock_nr_pages); +} + +/* Checks if this range of memory is likely to be hot-removable. */ +int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages) +{ + int type; + struct page *page = pfn_to_page(start_pfn); + struct page *end_page = page + nr_pages; + + /* Check the starting page of each pageblock within the range */ + for (; page < end_page; page = next_active_pageblock(page)) { + type = get_pageblock_migratetype(page); + + /* + * A pageblock containing MOVABLE or free pages is considered + * removable + */ + if (type != MIGRATE_MOVABLE && !pageblock_free(page)) + return 0; + + /* + * A pageblock starting with a PageReserved page is not + * considered removable. + */ + if (PageReserved(page)) + return 0; + } + + /* All pageblocks in the memory block are likely to be hot-removable */ + return 1; +} + /* * Confirm all pages in a range [start, end) is belongs to the same zone. */ -- cgit From 9ca908f47bc784c90e17a553ce33e756c73feac4 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Wed, 23 Jul 2008 21:28:20 -0700 Subject: kcalloc: remove runtime division While in all cases in the kernel we know the size of the elements to be created, we don't always know the count of elements. By commuting the size and count in the overflow check, the compiler can reduce the runtime division of size_t with a compare to a (unique) constant in these cases. Signed-off-by: Milton Miller Cc: Takashi Iwai Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/slab.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/slab.h b/include/linux/slab.h index 9aa90a6f20e0..41103910f8a2 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -180,7 +180,7 @@ size_t ksize(const void *); */ static inline void *kcalloc(size_t n, size_t size, gfp_t flags) { - if (n != 0 && size > ULONG_MAX / n) + if (size != 0 && n > ULONG_MAX / size) return NULL; return __kmalloc(n * size, flags | __GFP_ZERO); } -- cgit From 83d1674a946141c3c59d430e96c224f7937e6158 Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Wed, 23 Jul 2008 21:28:22 -0700 Subject: mm: make CONFIG_MIGRATION available w/o CONFIG_NUMA We'd like to support CONFIG_MEMORY_HOTREMOVE on s390, which depends on CONFIG_MIGRATION. So far, CONFIG_MIGRATION is only available with NUMA support. This patch makes CONFIG_MIGRATION selectable for architectures that define ARCH_ENABLE_MEMORY_HOTREMOVE. When MIGRATION is enabled w/o NUMA, the kernel won't compile because migrate_vmas() does not know about vm_ops->migrate() and vma_migratable() does not know about policy_zone. To fix this, those two functions can be restricted to '#ifdef CONFIG_NUMA' because they are not being used w/o NUMA. vma_migratable() is moved over from migrate.h to mempolicy.h. [kosaki.motohiro@jp.fujitsu.com: build fix] Acked-by: Christoph Lameter Signed-off-by: Gerald Schaefer Cc: Martin Schwidefsky Cc: Heiko Carstens Signed-off-by: KOSAKI Motorhiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mempolicy.h | 19 +++++++++++++++++++ include/linux/migrate.h | 21 --------------------- mm/Kconfig | 2 +- mm/migrate.c | 2 +- 4 files changed, 21 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h index 3a39570b81b8..085c903fe0f1 100644 --- a/include/linux/mempolicy.h +++ b/include/linux/mempolicy.h @@ -59,6 +59,7 @@ enum { #include #include #include +#include struct mm_struct; @@ -220,6 +221,24 @@ extern int mpol_parse_str(char *str, struct mempolicy **mpol, int no_context); extern int mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol, int no_context); #endif + +/* Check if a vma is migratable */ +static inline int vma_migratable(struct vm_area_struct *vma) +{ + if (vma->vm_flags & (VM_IO|VM_HUGETLB|VM_PFNMAP|VM_RESERVED)) + return 0; + /* + * Migration allocates pages in the highest zone. If we cannot + * do so then migration (at least from node to node) is not + * possible. + */ + if (vma->vm_file && + gfp_zone(mapping_gfp_mask(vma->vm_file->f_mapping)) + < policy_zone) + return 0; + return 1; +} + #else struct mempolicy {}; diff --git a/include/linux/migrate.h b/include/linux/migrate.h index e10a90a93b5d..03aea612d284 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h @@ -3,28 +3,10 @@ #include #include -#include typedef struct page *new_page_t(struct page *, unsigned long private, int **); #ifdef CONFIG_MIGRATION -/* Check if a vma is migratable */ -static inline int vma_migratable(struct vm_area_struct *vma) -{ - if (vma->vm_flags & (VM_IO|VM_HUGETLB|VM_PFNMAP|VM_RESERVED)) - return 0; - /* - * Migration allocates pages in the highest zone. If we cannot - * do so then migration (at least from node to node) is not - * possible. - */ - if (vma->vm_file && - gfp_zone(mapping_gfp_mask(vma->vm_file->f_mapping)) - < policy_zone) - return 0; - return 1; -} - extern int isolate_lru_page(struct page *p, struct list_head *pagelist); extern int putback_lru_pages(struct list_head *l); extern int migrate_page(struct address_space *, @@ -39,9 +21,6 @@ extern int migrate_vmas(struct mm_struct *mm, const nodemask_t *from, const nodemask_t *to, unsigned long flags); #else -static inline int vma_migratable(struct vm_area_struct *vma) - { return 0; } - static inline int isolate_lru_page(struct page *p, struct list_head *list) { return -ENOSYS; } static inline int putback_lru_pages(struct list_head *l) { return 0; } diff --git a/mm/Kconfig b/mm/Kconfig index c4de85285bb4..aa799007a11b 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -174,7 +174,7 @@ config SPLIT_PTLOCK_CPUS config MIGRATION bool "Page migration" def_bool y - depends on NUMA + depends on NUMA || ARCH_ENABLE_MEMORY_HOTREMOVE help Allows the migration of the physical location of pages of processes while the virtual addresses are not changed. This is useful for diff --git a/mm/migrate.c b/mm/migrate.c index e7d13a708da0..376cceba82f9 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1071,7 +1071,6 @@ out2: mmput(mm); return err; } -#endif /* * Call migration functions in the vma_ops that may prepare @@ -1093,3 +1092,4 @@ int migrate_vmas(struct mm_struct *mm, const nodemask_t *to, } return err; } +#endif -- cgit From 5459c164f0591ee75ed0203bb8f3817f25948e2f Mon Sep 17 00:00:00 2001 From: "Andrew G. Morgan" Date: Wed, 23 Jul 2008 21:28:24 -0700 Subject: security: protect legacy applications from executing with insufficient privilege When cap_bset suppresses some of the forced (fP) capabilities of a file, it is generally only safe to execute the program if it understands how to recognize it doesn't have enough privilege to work correctly. For legacy applications (fE!=0), which have no non-destructive way to determine that they are missing privilege, we fail to execute (EPERM) any executable that requires fP capabilities, but would otherwise get pP' < fP. This is a fail-safe permission check. For some discussion of why it is problematic for (legacy) privileged applications to run with less than the set of capabilities requested for them, see: http://userweb.kernel.org/~morgan/sendmail-capabilities-war-story.html With this iteration of this support, we do not include setuid-0 based privilege protection from the bounding set. That is, the admin can still (ab)use the bounding set to suppress the privileges of a setuid-0 program. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: cleanup] Signed-off-by: Andrew G. Morgan Acked-by: Serge Hallyn Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/binfmts.h | 2 +- security/commoncap.c | 108 ++++++++++++++++++++++++++---------------------- 2 files changed, 60 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index ee0ed48e8348..826f62350805 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -38,7 +38,7 @@ struct linux_binprm{ misc_bang:1; struct file * file; int e_uid, e_gid; - kernel_cap_t cap_inheritable, cap_permitted; + kernel_cap_t cap_post_exec_permitted; bool cap_effective; void *security; int argc, envc; diff --git a/security/commoncap.c b/security/commoncap.c index 0b6537a3672d..4afbece37a08 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -162,8 +162,7 @@ void cap_capset_set (struct task_struct *target, kernel_cap_t *effective, static inline void bprm_clear_caps(struct linux_binprm *bprm) { - cap_clear(bprm->cap_inheritable); - cap_clear(bprm->cap_permitted); + cap_clear(bprm->cap_post_exec_permitted); bprm->cap_effective = false; } @@ -198,6 +197,7 @@ static inline int cap_from_disk(struct vfs_cap_data *caps, { __u32 magic_etc; unsigned tocopy, i; + int ret; if (size < sizeof(magic_etc)) return -EINVAL; @@ -225,19 +225,40 @@ static inline int cap_from_disk(struct vfs_cap_data *caps, bprm->cap_effective = false; } - for (i = 0; i < tocopy; ++i) { - bprm->cap_permitted.cap[i] = - le32_to_cpu(caps->data[i].permitted); - bprm->cap_inheritable.cap[i] = - le32_to_cpu(caps->data[i].inheritable); - } - while (i < VFS_CAP_U32) { - bprm->cap_permitted.cap[i] = 0; - bprm->cap_inheritable.cap[i] = 0; - i++; + ret = 0; + + CAP_FOR_EACH_U32(i) { + __u32 value_cpu; + + if (i >= tocopy) { + /* + * Legacy capability sets have no upper bits + */ + bprm->cap_post_exec_permitted.cap[i] = 0; + continue; + } + /* + * pP' = (X & fP) | (pI & fI) + */ + value_cpu = le32_to_cpu(caps->data[i].permitted); + bprm->cap_post_exec_permitted.cap[i] = + (current->cap_bset.cap[i] & value_cpu) | + (current->cap_inheritable.cap[i] & + le32_to_cpu(caps->data[i].inheritable)); + if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) { + /* + * insufficient to execute correctly + */ + ret = -EPERM; + } } - return 0; + /* + * For legacy apps, with no internal support for recognizing they + * do not have enough capabilities, we return an error if they are + * missing some "forced" (aka file-permitted) capabilities. + */ + return bprm->cap_effective ? ret : 0; } /* Locate any VFS capabilities: */ @@ -269,9 +290,9 @@ static int get_file_caps(struct linux_binprm *bprm) goto out; rc = cap_from_disk(&vcaps, bprm, rc); - if (rc) + if (rc == -EINVAL) printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", - __func__, rc, bprm->filename); + __func__, rc, bprm->filename); out: dput(dentry); @@ -304,25 +325,24 @@ int cap_bprm_set_security (struct linux_binprm *bprm) int ret; ret = get_file_caps(bprm); - if (ret) - printk(KERN_NOTICE "%s: get_file_caps returned %d for %s\n", - __func__, ret, bprm->filename); - - /* To support inheritance of root-permissions and suid-root - * executables under compatibility mode, we raise all three - * capability sets for the file. - * - * If only the real uid is 0, we only raise the inheritable - * and permitted sets of the executable file. - */ - if (!issecure (SECURE_NOROOT)) { + if (!issecure(SECURE_NOROOT)) { + /* + * To support inheritance of root-permissions and suid-root + * executables under compatibility mode, we override the + * capability sets for the file. + * + * If only the real uid is 0, we do not set the effective + * bit. + */ if (bprm->e_uid == 0 || current->uid == 0) { - cap_set_full (bprm->cap_inheritable); - cap_set_full (bprm->cap_permitted); + /* pP' = (cap_bset & ~0) | (pI & ~0) */ + bprm->cap_post_exec_permitted = cap_combine( + current->cap_bset, current->cap_inheritable + ); + bprm->cap_effective = (bprm->e_uid == 0); + ret = 0; } - if (bprm->e_uid == 0) - bprm->cap_effective = true; } return ret; @@ -330,17 +350,9 @@ int cap_bprm_set_security (struct linux_binprm *bprm) void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) { - /* Derived from fs/exec.c:compute_creds. */ - kernel_cap_t new_permitted, working; - - new_permitted = cap_intersect(bprm->cap_permitted, - current->cap_bset); - working = cap_intersect(bprm->cap_inheritable, - current->cap_inheritable); - new_permitted = cap_combine(new_permitted, working); - if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || - !cap_issubset (new_permitted, current->cap_permitted)) { + !cap_issubset(bprm->cap_post_exec_permitted, + current->cap_permitted)) { set_dumpable(current->mm, suid_dumpable); current->pdeath_signal = 0; @@ -350,9 +362,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) bprm->e_gid = current->gid; } if (cap_limit_ptraced_target()) { - new_permitted = - cap_intersect(new_permitted, - current->cap_permitted); + bprm->cap_post_exec_permitted = cap_intersect( + bprm->cap_post_exec_permitted, + current->cap_permitted); } } } @@ -364,9 +376,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) * in the init_task struct. Thus we skip the usual * capability rules */ if (!is_global_init(current)) { - current->cap_permitted = new_permitted; + current->cap_permitted = bprm->cap_post_exec_permitted; if (bprm->cap_effective) - current->cap_effective = new_permitted; + current->cap_effective = bprm->cap_post_exec_permitted; else cap_clear(current->cap_effective); } @@ -381,9 +393,7 @@ int cap_bprm_secureexec (struct linux_binprm *bprm) if (current->uid != 0) { if (bprm->cap_effective) return 1; - if (!cap_isclear(bprm->cap_permitted)) - return 1; - if (!cap_isclear(bprm->cap_inheritable)) + if (!cap_isclear(bprm->cap_post_exec_permitted)) return 1; } -- cgit From 9b3e43a747c74029b0acf6acf4666601f132f471 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 23 Jul 2008 21:28:26 -0700 Subject: security: remove unused forwards Why would linux/security.h need forward declarations for nfsctl_arg and swap_info_struct? It's hard to imagine: remove them. Signed-off-by: Hugh Dickins Acked-by: James Morris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/security.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index 31c8851ec5d0..f0e9adb22ac2 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -102,9 +102,7 @@ extern unsigned long mmap_min_addr; #define LSM_SETID_FS 8 /* forward declares to avoid warnings */ -struct nfsctl_arg; struct sched_param; -struct swap_info_struct; struct request_sock; /* bprm_apply_creds unsafe reasons */ -- cgit From d75f65fd247fe85d90a3880d143b1bb22fe13a48 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 23 Jul 2008 21:28:34 -0700 Subject: remove include/linux/pm_legacy.h Remove the obsolete and no longer used include/linux/pm_legacy.h Reviewed-by: Robert P. J. Day Signed-off-by: Adrian Bunk Cc: Pavel Machek Acked-by: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/frv/kernel/pm.c | 1 - arch/mips/au1000/common/power.c | 1 - arch/x86/kernel/apm_32.c | 1 - include/linux/pm_legacy.h | 35 ----------------------------------- 4 files changed, 38 deletions(-) delete mode 100644 include/linux/pm_legacy.h (limited to 'include/linux') diff --git a/arch/frv/kernel/pm.c b/arch/frv/kernel/pm.c index 73f3aeefd203..d1113c5031f5 100644 --- a/arch/frv/kernel/pm.c +++ b/arch/frv/kernel/pm.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c index 2166b9e1e80c..bd854a6d1d89 100644 --- a/arch/mips/au1000/common/power.c +++ b/arch/mips/au1000/common/power.c @@ -31,7 +31,6 @@ #include #include -#include #include #include diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index bf9b441331e9..9ee24e6bc4b0 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -219,7 +219,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/pm_legacy.h b/include/linux/pm_legacy.h deleted file mode 100644 index 446f4f42b952..000000000000 --- a/include/linux/pm_legacy.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef __LINUX_PM_LEGACY_H__ -#define __LINUX_PM_LEGACY_H__ - - -#ifdef CONFIG_PM_LEGACY - -/* - * Register a device with power management - */ -struct pm_dev __deprecated * -pm_register(pm_dev_t type, unsigned long id, pm_callback callback); - -/* - * Send a request to all devices - */ -int __deprecated pm_send_all(pm_request_t rqst, void *data); - -#else /* CONFIG_PM_LEGACY */ - -static inline struct pm_dev *pm_register(pm_dev_t type, - unsigned long id, - pm_callback callback) -{ - return NULL; -} - -static inline int pm_send_all(pm_request_t rqst, void *data) -{ - return 0; -} - -#endif /* CONFIG_PM_LEGACY */ - -#endif /* __LINUX_PM_LEGACY_H__ */ - -- cgit From 558481f038e587b22d02167af58914c814ce9de5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Jul 2008 21:28:35 -0700 Subject: pm: remove definition of struct pm_dev Remove the definition of 'struct pm_dev', which is not used any more, along with some related stuff from include/linux/pm.h . Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pm.h | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pm.h b/include/linux/pm.h index 4ad9de94449a..5bf1ce89cfbb 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -68,30 +68,6 @@ enum */ #define PM_PCI_ID(dev) ((dev)->bus->number << 16 | (dev)->devfn) -/* - * Request handler callback - */ -struct pm_dev; - -typedef int (*pm_callback)(struct pm_dev *dev, pm_request_t rqst, void *data); - -/* - * Dynamic device information - */ -struct pm_dev -{ - pm_dev_t type; - unsigned long id; - pm_callback callback; - void *data; - - unsigned long flags; - unsigned long state; - unsigned long prev_state; - - struct list_head entry; -}; - /* Functions above this comment are list-based old-style power * management. Please avoid using them. */ -- cgit From e7ecb331e11d1f7aa66aeef9170fc20781c9bb55 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Jul 2008 21:28:35 -0700 Subject: pm: remove remaining obsolete definitions from pm.h Remove the remaining obsolete definitions from include/linux/pm.h and move the definitions of PM_SUSPEND and PM_RESUME to the header of h3600 which is the only user of them. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-arm/arch-sa1100/h3600.h | 5 ++++ include/linux/pm.h | 46 ------------------------------------- 2 files changed, 5 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/asm-arm/arch-sa1100/h3600.h b/include/asm-arm/arch-sa1100/h3600.h index 1b6355971574..3ca0ecf095e6 100644 --- a/include/asm-arm/arch-sa1100/h3600.h +++ b/include/asm-arm/arch-sa1100/h3600.h @@ -23,6 +23,11 @@ #ifndef _INCLUDE_H3600_H_ #define _INCLUDE_H3600_H_ +typedef int __bitwise pm_request_t; + +#define PM_SUSPEND ((__force pm_request_t) 1) /* enter D1-D3 */ +#define PM_RESUME ((__force pm_request_t) 2) /* enter D0 */ + /* generalized support for H3xxx series Compaq Pocket PC's */ #define machine_is_h3xxx() (machine_is_h3100() || machine_is_h3600() || machine_is_h3800()) diff --git a/include/linux/pm.h b/include/linux/pm.h index 5bf1ce89cfbb..390dd95a375e 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -25,52 +25,6 @@ #include #include -/* - * Power management requests... these are passed to pm_send_all() and friends. - * - * these functions are old and deprecated, see below. - */ -typedef int __bitwise pm_request_t; - -#define PM_SUSPEND ((__force pm_request_t) 1) /* enter D1-D3 */ -#define PM_RESUME ((__force pm_request_t) 2) /* enter D0 */ - - -/* - * Device types... these are passed to pm_register - */ -typedef int __bitwise pm_dev_t; - -#define PM_UNKNOWN_DEV ((__force pm_dev_t) 0) /* generic */ -#define PM_SYS_DEV ((__force pm_dev_t) 1) /* system device (fan, KB controller, ...) */ -#define PM_PCI_DEV ((__force pm_dev_t) 2) /* PCI device */ -#define PM_USB_DEV ((__force pm_dev_t) 3) /* USB device */ -#define PM_SCSI_DEV ((__force pm_dev_t) 4) /* SCSI device */ -#define PM_ISA_DEV ((__force pm_dev_t) 5) /* ISA device */ -#define PM_MTD_DEV ((__force pm_dev_t) 6) /* Memory Technology Device */ - -/* - * System device hardware ID (PnP) values - */ -enum -{ - PM_SYS_UNKNOWN = 0x00000000, /* generic */ - PM_SYS_KBC = 0x41d00303, /* keyboard controller */ - PM_SYS_COM = 0x41d00500, /* serial port */ - PM_SYS_IRDA = 0x41d00510, /* IRDA controller */ - PM_SYS_FDC = 0x41d00700, /* floppy controller */ - PM_SYS_VGA = 0x41d00900, /* VGA controller */ - PM_SYS_PCMCIA = 0x41d00e00, /* PCMCIA controller */ -}; - -/* - * Device identifier - */ -#define PM_PCI_ID(dev) ((dev)->bus->number << 16 | (dev)->devfn) - -/* Functions above this comment are list-based old-style power - * management. Please avoid using them. */ - /* * Callbacks for platform drivers to implement. */ -- cgit From 8c363265d57d755e62053e9f69a1f2164e83f7ea Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Jul 2008 21:28:37 -0700 Subject: pm: drop unnecessary includes from pm.h Drop unnecessary includes from include/linux/pm.h . Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pm.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pm.h b/include/linux/pm.h index 390dd95a375e..ed98d967f9fb 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -22,8 +22,6 @@ #define _LINUX_PM_H #include -#include -#include /* * Callbacks for platform drivers to implement. -- cgit From 8111d1b552349921aae1acf73e4e8cea98e80970 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 23 Jul 2008 21:28:37 -0700 Subject: pm: add new PM_EVENT codes for runtime power transitions This patch (as1112) adds some new PM_EVENT_* codes for use by kernel subsystems. They describe runtime power-state transitions of the sort already implemented by the USB subsystem. Signed-off-by: Alan Stern Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pm.h | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pm.h b/include/linux/pm.h index ed98d967f9fb..4dcce54b6d76 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -245,6 +245,21 @@ struct pm_ext_ops { * RECOVER Creation of a hibernation image or restoration of the main * memory contents from a hibernation image has failed, call * ->thaw() and ->complete() for all devices. + * + * The following PM_EVENT_ messages are defined for internal use by + * kernel subsystems. They are never issued by the PM core. + * + * USER_SUSPEND Manual selective suspend was issued by userspace. + * + * USER_RESUME Manual selective resume was issued by userspace. + * + * REMOTE_WAKEUP Remote-wakeup request was received from the device. + * + * AUTO_SUSPEND Automatic (device idle) runtime suspend was + * initiated by the subsystem. + * + * AUTO_RESUME Automatic (device needed) runtime resume was + * requested by a driver. */ #define PM_EVENT_ON 0x0000 @@ -256,9 +271,18 @@ struct pm_ext_ops { #define PM_EVENT_THAW 0x0020 #define PM_EVENT_RESTORE 0x0040 #define PM_EVENT_RECOVER 0x0080 +#define PM_EVENT_USER 0x0100 +#define PM_EVENT_REMOTE 0x0200 +#define PM_EVENT_AUTO 0x0400 -#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) +#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) +#define PM_EVENT_USER_SUSPEND (PM_EVENT_USER | PM_EVENT_SUSPEND) +#define PM_EVENT_USER_RESUME (PM_EVENT_USER | PM_EVENT_RESUME) +#define PM_EVENT_REMOTE_WAKEUP (PM_EVENT_REMOTE | PM_EVENT_RESUME) +#define PM_EVENT_AUTO_SUSPEND (PM_EVENT_AUTO | PM_EVENT_SUSPEND) +#define PM_EVENT_AUTO_RESUME (PM_EVENT_AUTO | PM_EVENT_RESUME) +#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) #define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) #define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, }) #define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, }) @@ -267,7 +291,16 @@ struct pm_ext_ops { #define PMSG_THAW ((struct pm_message){ .event = PM_EVENT_THAW, }) #define PMSG_RESTORE ((struct pm_message){ .event = PM_EVENT_RESTORE, }) #define PMSG_RECOVER ((struct pm_message){ .event = PM_EVENT_RECOVER, }) -#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) +#define PMSG_USER_SUSPEND ((struct pm_messge) \ + { .event = PM_EVENT_USER_SUSPEND, }) +#define PMSG_USER_RESUME ((struct pm_messge) \ + { .event = PM_EVENT_USER_RESUME, }) +#define PMSG_REMOTE_RESUME ((struct pm_messge) \ + { .event = PM_EVENT_REMOTE_RESUME, }) +#define PMSG_AUTO_SUSPEND ((struct pm_messge) \ + { .event = PM_EVENT_AUTO_SUSPEND, }) +#define PMSG_AUTO_RESUME ((struct pm_messge) \ + { .event = PM_EVENT_AUTO_RESUME, }) /** * Device power management states -- cgit From c1a220e7acf8ad2c03504891f4a70cd9c32c904b Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 23 Jul 2008 21:28:39 -0700 Subject: pm: introduce new interfaces schedule_work_on() and queue_work_on() This interface allows adding a job on a specific cpu. Although a work struct on a cpu will be scheduled to other cpu if the cpu dies, there is a recursion if a work task tries to offline the cpu it's running on. we need to schedule the task to a specific cpu in this case. http://bugzilla.kernel.org/show_bug.cgi?id=10897 [oleg@tv-sign.ru: cleanups] Signed-off-by: Zhang Rui Tested-by: Rus Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/workqueue.h | 3 +++ kernel/workqueue.c | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 542526c6e8ef..14d47120682b 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -179,6 +179,8 @@ __create_workqueue_key(const char *name, int singlethread, extern void destroy_workqueue(struct workqueue_struct *wq); extern int queue_work(struct workqueue_struct *wq, struct work_struct *work); +extern int queue_work_on(int cpu, struct workqueue_struct *wq, + struct work_struct *work); extern int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, @@ -188,6 +190,7 @@ extern void flush_workqueue(struct workqueue_struct *wq); extern void flush_scheduled_work(void); extern int schedule_work(struct work_struct *work); +extern int schedule_work_on(int cpu, struct work_struct *work); extern int schedule_delayed_work(struct delayed_work *work, unsigned long delay); extern int schedule_delayed_work_on(int cpu, struct delayed_work *work, unsigned long delay); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index a6d36346d10a..6fd158b21026 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -140,7 +140,6 @@ static void insert_work(struct cpu_workqueue_struct *cwq, wake_up(&cwq->more_work); } -/* Preempt must be disabled. */ static void __queue_work(struct cpu_workqueue_struct *cwq, struct work_struct *work) { @@ -175,6 +174,31 @@ int queue_work(struct workqueue_struct *wq, struct work_struct *work) } EXPORT_SYMBOL_GPL(queue_work); +/** + * queue_work_on - queue work on specific cpu + * @cpu: CPU number to execute work on + * @wq: workqueue to use + * @work: work to queue + * + * Returns 0 if @work was already on a queue, non-zero otherwise. + * + * We queue the work to a specific CPU, the caller must ensure it + * can't go away. + */ +int +queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work) +{ + int ret = 0; + + if (!test_and_set_bit(WORK_STRUCT_PENDING, work_data_bits(work))) { + BUG_ON(!list_empty(&work->entry)); + __queue_work(wq_per_cpu(wq, cpu), work); + ret = 1; + } + return ret; +} +EXPORT_SYMBOL_GPL(queue_work_on); + static void delayed_work_timer_fn(unsigned long __data) { struct delayed_work *dwork = (struct delayed_work *)__data; @@ -553,6 +577,19 @@ int schedule_work(struct work_struct *work) } EXPORT_SYMBOL(schedule_work); +/* + * schedule_work_on - put work task on a specific cpu + * @cpu: cpu to put the work task on + * @work: job to be done + * + * This puts a job on a specific cpu + */ +int schedule_work_on(int cpu, struct work_struct *work) +{ + return queue_work_on(cpu, keventd_wq, work); +} +EXPORT_SYMBOL(schedule_work_on); + /** * schedule_delayed_work - put work task in global workqueue after delay * @dwork: job to be done -- cgit From bdfe6b7c681669148dae4db27eb24ee5408ba371 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Wed, 23 Jul 2008 21:28:41 -0700 Subject: pm: acpi hibernation: utilize hardware signature ACPI defines a hardware signature. BIOS calculates the signature according to hardware configure and if hardware changes while hibernated, the signature will change. In that case, S4 resume should fail. Still, there may be systems on which this mechanism does not work correctly, so it is better to provide a workaround for them. For this reason, add a new switch to the acpi_sleep= command line argument allowing one to disable hardware signature checking. [shaohua.li@intel.com: build fix] Signed-off-by: Shaohua Li Signed-off-by: Rafael J. Wysocki Cc: Andi Kleen Cc: Len Brown Acked-by: Pavel Machek Cc: Cc: Shaohua Li Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 4 +++- arch/x86/kernel/acpi/sleep.c | 4 ++++ drivers/acpi/sleep/main.c | 22 ++++++++++++++++++++++ include/linux/acpi.h | 1 + 4 files changed, 30 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 4d705713cabc..497a98dafdaa 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -148,10 +148,12 @@ and is between 256 and 4096 characters. It is defined in the file default: 0 acpi_sleep= [HW,ACPI] Sleep options - Format: { s3_bios, s3_mode, s3_beep, old_ordering } + Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering } See Documentation/power/video.txt for s3_bios and s3_mode. s3_beep is for debugging; it makes the PC's speaker beep as soon as the kernel's real-mode entry point is called. + s4_nohwsig prevents ACPI hardware signature from being + used during resume from hibernation. old_ordering causes the ACPI 1.0 ordering of the _PTS control method, wrt putting devices into low power states, to be enforced (the ACPI 2.0 ordering of _PTS is diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index a3ddad18aaa3..fa2161d5003b 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -150,6 +150,10 @@ static int __init acpi_sleep_setup(char *str) acpi_realmode_flags |= 2; if (strncmp(str, "s3_beep", 7) == 0) acpi_realmode_flags |= 4; +#ifdef CONFIG_HIBERNATION + if (strncmp(str, "s4_nohwsig", 10) == 0) + acpi_no_s4_hw_signature(); +#endif if (strncmp(str, "old_ordering", 12) == 0) acpi_old_suspend_ordering(); str = strchr(str, ','); diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 0489a7d1d42c..313507accf18 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -283,6 +283,15 @@ static struct platform_suspend_ops acpi_suspend_ops_old = { #endif /* CONFIG_SUSPEND */ #ifdef CONFIG_HIBERNATION +static unsigned long s4_hardware_signature; +static struct acpi_table_facs *facs; +static bool nosigcheck; + +void __init acpi_no_s4_hw_signature(void) +{ + nosigcheck = true; +} + static int acpi_hibernation_begin(void) { acpi_target_sleep_state = ACPI_STATE_S4; @@ -316,6 +325,12 @@ static void acpi_hibernation_leave(void) acpi_enable(); /* Reprogram control registers and execute _BFS */ acpi_leave_sleep_state_prep(ACPI_STATE_S4); + /* Check the hardware signature */ + if (facs && s4_hardware_signature != facs->hardware_signature) { + printk(KERN_EMERG "ACPI: Hardware changed while hibernated, " + "cannot resume!\n"); + panic("ACPI S4 hardware signature mismatch"); + } } static void acpi_pm_enable_gpes(void) @@ -544,6 +559,13 @@ int __init acpi_sleep_init(void) &acpi_hibernation_ops_old : &acpi_hibernation_ops); sleep_states[ACPI_STATE_S4] = 1; printk(" S4"); + if (!nosigcheck) { + acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS, + (struct acpi_table_header **)&facs); + if (facs) + s4_hardware_signature = + facs->hardware_signature; + } } #endif status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index a17177639376..702f79dad16a 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -236,6 +236,7 @@ int acpi_check_mem_region(resource_size_t start, resource_size_t n, const char *name); #ifdef CONFIG_PM_SLEEP +void __init acpi_no_s4_hw_signature(void); void __init acpi_old_suspend_ordering(void); #endif /* CONFIG_PM_SLEEP */ #else /* CONFIG_ACPI */ -- cgit From f606ddf42fd4edc558eeb48bfee66d2c591571d2 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 23 Jul 2008 21:28:50 -0700 Subject: remove the v850 port Trying to compile the v850 port brings many compile errors, one of them exists since at least kernel 2.6.19. There also seems to be noone willing to bring this port back into a usable state. This patch therefore removes the v850 port. If anyone ever decides to revive the v850 port the code will still be available from older kernels, and it wouldn't be impossible for the port to reenter the kernel if it would become actively maintained again. Signed-off-by: Adrian Bunk Acked-by: Greg Ungerer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 3 - arch/v850/Kconfig | 353 ---------- arch/v850/Kconfig.debug | 10 - arch/v850/Makefile | 54 -- arch/v850/README | 44 -- arch/v850/configs/rte-ma1-cb_defconfig | 617 ------------------ arch/v850/configs/rte-me2-cb_defconfig | 462 ------------- arch/v850/configs/sim_defconfig | 451 ------------- arch/v850/kernel/Makefile | 40 -- arch/v850/kernel/anna-rom.ld | 16 - arch/v850/kernel/anna.c | 202 ------ arch/v850/kernel/anna.ld | 20 - arch/v850/kernel/as85ep1-rom.ld | 21 - arch/v850/kernel/as85ep1.c | 234 ------- arch/v850/kernel/as85ep1.ld | 49 -- arch/v850/kernel/asm-offsets.c | 58 -- arch/v850/kernel/bug.c | 142 ---- arch/v850/kernel/entry.S | 1121 -------------------------------- arch/v850/kernel/fpga85e2c.c | 167 ----- arch/v850/kernel/fpga85e2c.ld | 62 -- arch/v850/kernel/gbus_int.c | 271 -------- arch/v850/kernel/head.S | 128 ---- arch/v850/kernel/highres_timer.c | 132 ---- arch/v850/kernel/init_task.c | 48 -- arch/v850/kernel/intv.S | 87 --- arch/v850/kernel/irq.c | 123 ---- arch/v850/kernel/ma.c | 69 -- arch/v850/kernel/mach.c | 17 - arch/v850/kernel/mach.h | 56 -- arch/v850/kernel/me2.c | 73 --- arch/v850/kernel/memcons.c | 135 ---- arch/v850/kernel/module.c | 237 ------- arch/v850/kernel/process.c | 217 ------- arch/v850/kernel/procfs.c | 67 -- arch/v850/kernel/ptrace.c | 235 ------- arch/v850/kernel/rte_cb.c | 193 ------ arch/v850/kernel/rte_cb_leds.c | 137 ---- arch/v850/kernel/rte_cb_multi.c | 121 ---- arch/v850/kernel/rte_ma1_cb-rom.ld | 14 - arch/v850/kernel/rte_ma1_cb.c | 107 --- arch/v850/kernel/rte_ma1_cb.ld | 57 -- arch/v850/kernel/rte_mb_a_pci.c | 819 ----------------------- arch/v850/kernel/rte_me2_cb.c | 298 --------- arch/v850/kernel/rte_me2_cb.ld | 30 - arch/v850/kernel/rte_nb85e_cb-multi.ld | 57 -- arch/v850/kernel/rte_nb85e_cb.c | 81 --- arch/v850/kernel/rte_nb85e_cb.ld | 22 - arch/v850/kernel/setup.c | 329 ---------- arch/v850/kernel/signal.c | 523 --------------- arch/v850/kernel/sim.c | 172 ----- arch/v850/kernel/sim.ld | 13 - arch/v850/kernel/sim85e2.c | 195 ------ arch/v850/kernel/sim85e2.ld | 36 - arch/v850/kernel/simcons.c | 161 ----- arch/v850/kernel/syscalls.c | 196 ------ arch/v850/kernel/teg.c | 62 -- arch/v850/kernel/time.c | 106 --- arch/v850/kernel/v850_ksyms.c | 51 -- arch/v850/kernel/v850e2_cache.c | 127 ---- arch/v850/kernel/v850e_cache.c | 174 ----- arch/v850/kernel/v850e_intc.c | 104 --- arch/v850/kernel/v850e_timer_d.c | 54 -- arch/v850/kernel/v850e_utils.c | 62 -- arch/v850/kernel/vmlinux.lds.S | 306 --------- arch/v850/lib/Makefile | 6 - arch/v850/lib/ashldi3.c | 62 -- arch/v850/lib/ashrdi3.c | 63 -- arch/v850/lib/checksum.c | 155 ----- arch/v850/lib/lshrdi3.c | 62 -- arch/v850/lib/memcpy.c | 92 --- arch/v850/lib/memset.c | 68 -- arch/v850/lib/muldi3.c | 61 -- arch/v850/lib/negdi2.c | 25 - drivers/serial/Kconfig | 16 - drivers/watchdog/Kconfig | 2 - drivers/watchdog/Makefile | 2 - include/asm-v850/Kbuild | 1 - include/asm-v850/a.out.h | 21 - include/asm-v850/anna.h | 137 ---- include/asm-v850/as85ep1.h | 152 ----- include/asm-v850/asm.h | 32 - include/asm-v850/atomic.h | 131 ---- include/asm-v850/auxvec.h | 4 - include/asm-v850/bitops.h | 161 ----- include/asm-v850/bug.h | 25 - include/asm-v850/bugs.h | 16 - include/asm-v850/byteorder.h | 48 -- include/asm-v850/cache.h | 26 - include/asm-v850/cacheflush.h | 70 -- include/asm-v850/checksum.h | 112 ---- include/asm-v850/clinkage.h | 26 - include/asm-v850/cputime.h | 6 - include/asm-v850/current.h | 47 -- include/asm-v850/delay.h | 47 -- include/asm-v850/device.h | 7 - include/asm-v850/div64.h | 1 - include/asm-v850/dma-mapping.h | 11 - include/asm-v850/dma.h | 18 - include/asm-v850/elf.h | 99 --- include/asm-v850/emergency-restart.h | 6 - include/asm-v850/entry.h | 113 ---- include/asm-v850/errno.h | 6 - include/asm-v850/fb.h | 12 - include/asm-v850/fcntl.h | 11 - include/asm-v850/flat.h | 133 ---- include/asm-v850/fpga85e2c.h | 82 --- include/asm-v850/futex.h | 6 - include/asm-v850/gbus_int.h | 97 --- include/asm-v850/hardirq.h | 28 - include/asm-v850/highres_timer.h | 44 -- include/asm-v850/hw_irq.h | 4 - include/asm-v850/io.h | 142 ---- include/asm-v850/ioctl.h | 1 - include/asm-v850/ioctls.h | 84 --- include/asm-v850/ipcbuf.h | 29 - include/asm-v850/irq.h | 55 -- include/asm-v850/irq_regs.h | 1 - include/asm-v850/kdebug.h | 1 - include/asm-v850/kmap_types.h | 19 - include/asm-v850/kvm.h | 6 - include/asm-v850/linkage.h | 8 - include/asm-v850/local.h | 6 - include/asm-v850/ma.h | 101 --- include/asm-v850/ma1.h | 50 -- include/asm-v850/machdep.h | 60 -- include/asm-v850/macrology.h | 17 - include/asm-v850/me2.h | 182 ------ include/asm-v850/mman.h | 15 - include/asm-v850/mmu.h | 11 - include/asm-v850/mmu_context.h | 13 - include/asm-v850/module.h | 62 -- include/asm-v850/msgbuf.h | 31 - include/asm-v850/mutex.h | 9 - include/asm-v850/page.h | 124 ---- include/asm-v850/param.h | 33 - include/asm-v850/pci.h | 119 ---- include/asm-v850/percpu.h | 14 - include/asm-v850/pgalloc.h | 22 - include/asm-v850/pgtable.h | 59 -- include/asm-v850/poll.h | 9 - include/asm-v850/posix_types.h | 72 -- include/asm-v850/processor.h | 120 ---- include/asm-v850/ptrace.h | 121 ---- include/asm-v850/resource.h | 6 - include/asm-v850/rte_cb.h | 78 --- include/asm-v850/rte_ma1_cb.h | 128 ---- include/asm-v850/rte_mb_a_pci.h | 56 -- include/asm-v850/rte_me2_cb.h | 202 ------ include/asm-v850/rte_nb85e_cb.h | 111 ---- include/asm-v850/scatterlist.h | 31 - include/asm-v850/sections.h | 6 - include/asm-v850/segment.h | 36 - include/asm-v850/semaphore.h | 1 - include/asm-v850/sembuf.h | 25 - include/asm-v850/serial.h | 56 -- include/asm-v850/setup.h | 6 - include/asm-v850/shmbuf.h | 42 -- include/asm-v850/shmparam.h | 6 - include/asm-v850/sigcontext.h | 25 - include/asm-v850/siginfo.h | 6 - include/asm-v850/signal.h | 168 ----- include/asm-v850/sim.h | 47 -- include/asm-v850/sim85e2.h | 69 -- include/asm-v850/sim85e2c.h | 26 - include/asm-v850/sim85e2s.h | 28 - include/asm-v850/simsyscall.h | 99 --- include/asm-v850/socket.h | 57 -- include/asm-v850/sockios.h | 13 - include/asm-v850/stat.h | 73 --- include/asm-v850/statfs.h | 6 - include/asm-v850/string.h | 25 - include/asm-v850/system.h | 123 ---- include/asm-v850/teg.h | 101 --- include/asm-v850/termbits.h | 200 ------ include/asm-v850/termios.h | 90 --- include/asm-v850/thread_info.h | 129 ---- include/asm-v850/timex.h | 18 - include/asm-v850/tlb.h | 21 - include/asm-v850/tlbflush.h | 64 -- include/asm-v850/topology.h | 6 - include/asm-v850/types.h | 36 - include/asm-v850/uaccess.h | 159 ----- include/asm-v850/ucontext.h | 14 - include/asm-v850/unaligned.h | 22 - include/asm-v850/unistd.h | 244 ------- include/asm-v850/user.h | 52 -- include/asm-v850/v850e.h | 21 - include/asm-v850/v850e2.h | 69 -- include/asm-v850/v850e2_cache.h | 75 --- include/asm-v850/v850e_cache.h | 48 -- include/asm-v850/v850e_intc.h | 133 ---- include/asm-v850/v850e_timer_c.h | 48 -- include/asm-v850/v850e_timer_d.h | 62 -- include/asm-v850/v850e_uart.h | 76 --- include/asm-v850/v850e_uarta.h | 278 -------- include/asm-v850/v850e_uartb.h | 262 -------- include/asm-v850/v850e_utils.h | 35 - include/linux/audit.h | 1 - include/linux/module.h | 2 +- include/linux/serial_core.h | 3 - include/linux/syscalls.h | 2 +- scripts/genksyms/genksyms.c | 3 +- scripts/mod/file2alias.c | 2 +- scripts/mod/mk_elfconfig.c | 2 +- 204 files changed, 5 insertions(+), 18406 deletions(-) delete mode 100644 arch/v850/Kconfig delete mode 100644 arch/v850/Kconfig.debug delete mode 100644 arch/v850/Makefile delete mode 100644 arch/v850/README delete mode 100644 arch/v850/configs/rte-ma1-cb_defconfig delete mode 100644 arch/v850/configs/rte-me2-cb_defconfig delete mode 100644 arch/v850/configs/sim_defconfig delete mode 100644 arch/v850/kernel/Makefile delete mode 100644 arch/v850/kernel/anna-rom.ld delete mode 100644 arch/v850/kernel/anna.c delete mode 100644 arch/v850/kernel/anna.ld delete mode 100644 arch/v850/kernel/as85ep1-rom.ld delete mode 100644 arch/v850/kernel/as85ep1.c delete mode 100644 arch/v850/kernel/as85ep1.ld delete mode 100644 arch/v850/kernel/asm-offsets.c delete mode 100644 arch/v850/kernel/bug.c delete mode 100644 arch/v850/kernel/entry.S delete mode 100644 arch/v850/kernel/fpga85e2c.c delete mode 100644 arch/v850/kernel/fpga85e2c.ld delete mode 100644 arch/v850/kernel/gbus_int.c delete mode 100644 arch/v850/kernel/head.S delete mode 100644 arch/v850/kernel/highres_timer.c delete mode 100644 arch/v850/kernel/init_task.c delete mode 100644 arch/v850/kernel/intv.S delete mode 100644 arch/v850/kernel/irq.c delete mode 100644 arch/v850/kernel/ma.c delete mode 100644 arch/v850/kernel/mach.c delete mode 100644 arch/v850/kernel/mach.h delete mode 100644 arch/v850/kernel/me2.c delete mode 100644 arch/v850/kernel/memcons.c delete mode 100644 arch/v850/kernel/module.c delete mode 100644 arch/v850/kernel/process.c delete mode 100644 arch/v850/kernel/procfs.c delete mode 100644 arch/v850/kernel/ptrace.c delete mode 100644 arch/v850/kernel/rte_cb.c delete mode 100644 arch/v850/kernel/rte_cb_leds.c delete mode 100644 arch/v850/kernel/rte_cb_multi.c delete mode 100644 arch/v850/kernel/rte_ma1_cb-rom.ld delete mode 100644 arch/v850/kernel/rte_ma1_cb.c delete mode 100644 arch/v850/kernel/rte_ma1_cb.ld delete mode 100644 arch/v850/kernel/rte_mb_a_pci.c delete mode 100644 arch/v850/kernel/rte_me2_cb.c delete mode 100644 arch/v850/kernel/rte_me2_cb.ld delete mode 100644 arch/v850/kernel/rte_nb85e_cb-multi.ld delete mode 100644 arch/v850/kernel/rte_nb85e_cb.c delete mode 100644 arch/v850/kernel/rte_nb85e_cb.ld delete mode 100644 arch/v850/kernel/setup.c delete mode 100644 arch/v850/kernel/signal.c delete mode 100644 arch/v850/kernel/sim.c delete mode 100644 arch/v850/kernel/sim.ld delete mode 100644 arch/v850/kernel/sim85e2.c delete mode 100644 arch/v850/kernel/sim85e2.ld delete mode 100644 arch/v850/kernel/simcons.c delete mode 100644 arch/v850/kernel/syscalls.c delete mode 100644 arch/v850/kernel/teg.c delete mode 100644 arch/v850/kernel/time.c delete mode 100644 arch/v850/kernel/v850_ksyms.c delete mode 100644 arch/v850/kernel/v850e2_cache.c delete mode 100644 arch/v850/kernel/v850e_cache.c delete mode 100644 arch/v850/kernel/v850e_intc.c delete mode 100644 arch/v850/kernel/v850e_timer_d.c delete mode 100644 arch/v850/kernel/v850e_utils.c delete mode 100644 arch/v850/kernel/vmlinux.lds.S delete mode 100644 arch/v850/lib/Makefile delete mode 100644 arch/v850/lib/ashldi3.c delete mode 100644 arch/v850/lib/ashrdi3.c delete mode 100644 arch/v850/lib/checksum.c delete mode 100644 arch/v850/lib/lshrdi3.c delete mode 100644 arch/v850/lib/memcpy.c delete mode 100644 arch/v850/lib/memset.c delete mode 100644 arch/v850/lib/muldi3.c delete mode 100644 arch/v850/lib/negdi2.c delete mode 100644 include/asm-v850/Kbuild delete mode 100644 include/asm-v850/a.out.h delete mode 100644 include/asm-v850/anna.h delete mode 100644 include/asm-v850/as85ep1.h delete mode 100644 include/asm-v850/asm.h delete mode 100644 include/asm-v850/atomic.h delete mode 100644 include/asm-v850/auxvec.h delete mode 100644 include/asm-v850/bitops.h delete mode 100644 include/asm-v850/bug.h delete mode 100644 include/asm-v850/bugs.h delete mode 100644 include/asm-v850/byteorder.h delete mode 100644 include/asm-v850/cache.h delete mode 100644 include/asm-v850/cacheflush.h delete mode 100644 include/asm-v850/checksum.h delete mode 100644 include/asm-v850/clinkage.h delete mode 100644 include/asm-v850/cputime.h delete mode 100644 include/asm-v850/current.h delete mode 100644 include/asm-v850/delay.h delete mode 100644 include/asm-v850/device.h delete mode 100644 include/asm-v850/div64.h delete mode 100644 include/asm-v850/dma-mapping.h delete mode 100644 include/asm-v850/dma.h delete mode 100644 include/asm-v850/elf.h delete mode 100644 include/asm-v850/emergency-restart.h delete mode 100644 include/asm-v850/entry.h delete mode 100644 include/asm-v850/errno.h delete mode 100644 include/asm-v850/fb.h delete mode 100644 include/asm-v850/fcntl.h delete mode 100644 include/asm-v850/flat.h delete mode 100644 include/asm-v850/fpga85e2c.h delete mode 100644 include/asm-v850/futex.h delete mode 100644 include/asm-v850/gbus_int.h delete mode 100644 include/asm-v850/hardirq.h delete mode 100644 include/asm-v850/highres_timer.h delete mode 100644 include/asm-v850/hw_irq.h delete mode 100644 include/asm-v850/io.h delete mode 100644 include/asm-v850/ioctl.h delete mode 100644 include/asm-v850/ioctls.h delete mode 100644 include/asm-v850/ipcbuf.h delete mode 100644 include/asm-v850/irq.h delete mode 100644 include/asm-v850/irq_regs.h delete mode 100644 include/asm-v850/kdebug.h delete mode 100644 include/asm-v850/kmap_types.h delete mode 100644 include/asm-v850/kvm.h delete mode 100644 include/asm-v850/linkage.h delete mode 100644 include/asm-v850/local.h delete mode 100644 include/asm-v850/ma.h delete mode 100644 include/asm-v850/ma1.h delete mode 100644 include/asm-v850/machdep.h delete mode 100644 include/asm-v850/macrology.h delete mode 100644 include/asm-v850/me2.h delete mode 100644 include/asm-v850/mman.h delete mode 100644 include/asm-v850/mmu.h delete mode 100644 include/asm-v850/mmu_context.h delete mode 100644 include/asm-v850/module.h delete mode 100644 include/asm-v850/msgbuf.h delete mode 100644 include/asm-v850/mutex.h delete mode 100644 include/asm-v850/page.h delete mode 100644 include/asm-v850/param.h delete mode 100644 include/asm-v850/pci.h delete mode 100644 include/asm-v850/percpu.h delete mode 100644 include/asm-v850/pgalloc.h delete mode 100644 include/asm-v850/pgtable.h delete mode 100644 include/asm-v850/poll.h delete mode 100644 include/asm-v850/posix_types.h delete mode 100644 include/asm-v850/processor.h delete mode 100644 include/asm-v850/ptrace.h delete mode 100644 include/asm-v850/resource.h delete mode 100644 include/asm-v850/rte_cb.h delete mode 100644 include/asm-v850/rte_ma1_cb.h delete mode 100644 include/asm-v850/rte_mb_a_pci.h delete mode 100644 include/asm-v850/rte_me2_cb.h delete mode 100644 include/asm-v850/rte_nb85e_cb.h delete mode 100644 include/asm-v850/scatterlist.h delete mode 100644 include/asm-v850/sections.h delete mode 100644 include/asm-v850/segment.h delete mode 100644 include/asm-v850/semaphore.h delete mode 100644 include/asm-v850/sembuf.h delete mode 100644 include/asm-v850/serial.h delete mode 100644 include/asm-v850/setup.h delete mode 100644 include/asm-v850/shmbuf.h delete mode 100644 include/asm-v850/shmparam.h delete mode 100644 include/asm-v850/sigcontext.h delete mode 100644 include/asm-v850/siginfo.h delete mode 100644 include/asm-v850/signal.h delete mode 100644 include/asm-v850/sim.h delete mode 100644 include/asm-v850/sim85e2.h delete mode 100644 include/asm-v850/sim85e2c.h delete mode 100644 include/asm-v850/sim85e2s.h delete mode 100644 include/asm-v850/simsyscall.h delete mode 100644 include/asm-v850/socket.h delete mode 100644 include/asm-v850/sockios.h delete mode 100644 include/asm-v850/stat.h delete mode 100644 include/asm-v850/statfs.h delete mode 100644 include/asm-v850/string.h delete mode 100644 include/asm-v850/system.h delete mode 100644 include/asm-v850/teg.h delete mode 100644 include/asm-v850/termbits.h delete mode 100644 include/asm-v850/termios.h delete mode 100644 include/asm-v850/thread_info.h delete mode 100644 include/asm-v850/timex.h delete mode 100644 include/asm-v850/tlb.h delete mode 100644 include/asm-v850/tlbflush.h delete mode 100644 include/asm-v850/topology.h delete mode 100644 include/asm-v850/types.h delete mode 100644 include/asm-v850/uaccess.h delete mode 100644 include/asm-v850/ucontext.h delete mode 100644 include/asm-v850/unaligned.h delete mode 100644 include/asm-v850/unistd.h delete mode 100644 include/asm-v850/user.h delete mode 100644 include/asm-v850/v850e.h delete mode 100644 include/asm-v850/v850e2.h delete mode 100644 include/asm-v850/v850e2_cache.h delete mode 100644 include/asm-v850/v850e_cache.h delete mode 100644 include/asm-v850/v850e_intc.h delete mode 100644 include/asm-v850/v850e_timer_c.h delete mode 100644 include/asm-v850/v850e_timer_d.h delete mode 100644 include/asm-v850/v850e_uart.h delete mode 100644 include/asm-v850/v850e_uarta.h delete mode 100644 include/asm-v850/v850e_uartb.h delete mode 100644 include/asm-v850/v850e_utils.h (limited to 'include/linux') diff --git a/MAINTAINERS b/MAINTAINERS index 7ffd78c4e277..7e5c7b0290bb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4131,9 +4131,6 @@ W: http://www.uclinux.org/ L: uclinux-dev@uclinux.org (subscribers-only) S: Maintained -UCLINUX FOR NEC V850 -P: Miles Bader - UCLINUX FOR RENESAS H8/300 P: Yoshinori Sato M: ysato@users.sourceforge.jp diff --git a/arch/v850/Kconfig b/arch/v850/Kconfig deleted file mode 100644 index 4379f43505ef..000000000000 --- a/arch/v850/Kconfig +++ /dev/null @@ -1,353 +0,0 @@ -############################################################################# -# -# For a description of the syntax of this configuration file, -# see Documentation/kbuild/kconfig-language.txt. -# -############################################################################# - -mainmenu "uClinux/v850 (w/o MMU) Kernel Configuration" - -config MMU - bool - default n -config ZONE_DMA - bool - default y -config RWSEM_GENERIC_SPINLOCK - bool - default y -config RWSEM_XCHGADD_ALGORITHM - bool - default n -config GENERIC_FIND_NEXT_BIT - bool - default y -config GENERIC_HWEIGHT - bool - default y -config GENERIC_CALIBRATE_DELAY - bool - default y - -config GENERIC_HARDIRQS - bool - default y - -config GENERIC_IRQ_PROBE - bool - default y - -config GENERIC_TIME - bool - default y - -config TIME_LOW_RES - bool - default y - -config ARCH_HAS_ILOG2_U32 - bool - default n - -config ARCH_HAS_ILOG2_U64 - bool - default n - -config ARCH_SUPPORTS_AOUT - def_bool y - -# Turn off some random 386 crap that can affect device config -config ISA - bool - default n -config ISAPNP - bool - default n -config EISA - bool - default n -config MCA - bool - default n - - -############################################################################# -#### v850-specific config - -# Define the architecture -config V850 - bool - default y - select HAVE_IDE - -menu "Processor type and features" - - choice - prompt "Platform" - default GDB - config V850E_SIM - bool "GDB" - config RTE_CB_MA1 - bool "RTE-V850E/MA1-CB" - config RTE_CB_NB85E - bool "RTE-V850E/NB85E-CB" - config RTE_CB_ME2 - bool "RTE-V850E/ME2-CB" - config V850E_AS85EP1 - bool "AS85EP1" - config V850E2_SIM85E2C - bool "sim85e2c" - config V850E2_SIM85E2S - bool "sim85e2s" - config V850E2_FPGA85E2C - bool "NA85E2C-FPGA" - config V850E2_ANNA - bool "Anna" - endchoice - - #### V850E processor-specific config - - # All CPUs currently supported use the v850e architecture - config V850E - bool - default y - - # The RTE-V850E/MA1-CB is the only type of V850E/MA1 platform we - # currently support - config V850E_MA1 - bool - depends on RTE_CB_MA1 - default y - # Similarly for the RTE-V850E/NB85E-CB - V850E/TEG - config V850E_TEG - bool - depends on RTE_CB_NB85E - default y - # ... and the RTE-V850E/ME2-CB - V850E/ME2 - config V850E_ME2 - bool - depends on RTE_CB_ME2 - default y - - - #### sim85e2-specific config - - config V850E2_SIM85E2 - bool - depends on V850E2_SIM85E2C || V850E2_SIM85E2S - default y - - - #### V850E2 processor-specific config - - # V850E2 processors - config V850E2 - bool - depends on V850E2_SIM85E2 || V850E2_FPGA85E2C || V850E2_ANNA - default y - - - #### RTE-CB platform-specific config - - # Boards in the RTE-x-CB series - config RTE_CB - bool - depends on RTE_CB_MA1 || RTE_CB_NB85E || RTE_CB_ME2 - default y - - config RTE_CB_MULTI - bool - # RTE_CB_NB85E can either have multi ROM support or not, but - # other platforms (currently only RTE_CB_MA1) require it. - prompt "Multi monitor ROM support" if RTE_CB_NB85E - depends on RTE_CB_MA1 || RTE_CB_NB85E - default y - - config RTE_CB_MULTI_DBTRAP - bool "Pass illegal insn trap / dbtrap to kernel" - depends on RTE_CB_MULTI - default n - - config RTE_CB_MA1_KSRAM - bool "Kernel in SRAM (limits size of kernel)" - depends on RTE_CB_MA1 && RTE_CB_MULTI - default n - - config RTE_MB_A_PCI - bool "Mother-A PCI support" - depends on RTE_CB - default y - - # The GBUS is used to talk to the RTE-MOTHER-A board - config RTE_GBUS_INT - bool - depends on RTE_MB_A_PCI - default y - - # The only PCI bus we support is on the RTE-MOTHER-A board - config PCI - bool - default RTE_MB_A_PCI - - #### Some feature-specific configs - - # Everything except for the GDB simulator uses the same interrupt controller - config V850E_INTC - bool - default !V850E_SIM - - # Everything except for the various simulators uses the "Timer D" unit - config V850E_TIMER_D - bool - default !V850E_SIM && !V850E2_SIM85E2 - - # Cache control used on some v850e1 processors - config V850E_CACHE - bool - default V850E_TEG || V850E_ME2 - - # Cache control used on v850e2 processors; I think this should - # actually apply to more, but currently only the SIM85E2S uses it - config V850E2_CACHE - bool - default V850E2_SIM85E2S - - config NO_CACHE - bool - default !V850E_CACHE && !V850E2_CACHE - - # HZ depends on the platform - config HZ - int - default 24 if V850E_SIM || V850E2_SIM85E2 - default 122 if V850E2_FPGA85E2C - default 100 - - #### Misc config - - config ROM_KERNEL - bool "Kernel in ROM" - depends on V850E2_ANNA || V850E_AS85EP1 || RTE_CB_ME2 - - # Some platforms pre-zero memory, in which case the kernel doesn't need to - config ZERO_BSS - bool - depends on !V850E2_SIM85E2C - default y - - # The crappy-ass zone allocator requires that the start of allocatable - # memory be aligned to the largest possible allocation. - config FORCE_MAX_ZONEORDER - int - default 8 if V850E2_SIM85E2C || V850E2_FPGA85E2C - - config V850E_HIGHRES_TIMER - bool "High resolution timer support" - depends on V850E_TIMER_D - config TIME_BOOTUP - bool "Time bootup" - depends on V850E_HIGHRES_TIMER - - config RESET_GUARD - bool "Reset Guard" - -source "mm/Kconfig" - -endmenu - - -############################################################################# - -source init/Kconfig - -############################################################################# - -menu "Bus options (PCI, PCMCIA, EISA, MCA, ISA)" - -# config PCI -# bool "PCI support" -# help -# Support for PCI bus. - -source "drivers/pci/Kconfig" - -source "drivers/pcmcia/Kconfig" - -source "drivers/pci/hotplug/Kconfig" - -endmenu - -menu "Executable file formats" - -source "fs/Kconfig.binfmt" - -endmenu - -source "net/Kconfig" - -############################################################################# - -source "drivers/base/Kconfig" - -source drivers/mtd/Kconfig - -source drivers/parport/Kconfig - -#source drivers/pnp/Kconfig - -source drivers/block/Kconfig - -############################################################################# - -menu "Disk device support" - -source "drivers/ide/Kconfig" - -source "drivers/scsi/Kconfig" - -endmenu - -############################################################################# - - -source "drivers/md/Kconfig" - -source "drivers/message/fusion/Kconfig" - -source "drivers/ieee1394/Kconfig" - -source "drivers/message/i2o/Kconfig" - -source "drivers/net/Kconfig" - -source "drivers/isdn/Kconfig" - -#source "drivers/telephony/Kconfig" - -# -# input before char - char/joystick depends on it. As does USB. -# -source "drivers/input/Kconfig" - -source "drivers/char/Kconfig" - -#source drivers/misc/Config.in -source "drivers/media/Kconfig" - -source "fs/Kconfig" - -source "drivers/video/Kconfig" - -source "sound/Kconfig" - -source "drivers/usb/Kconfig" - -source "arch/v850/Kconfig.debug" - -source "security/Kconfig" - -source "crypto/Kconfig" - -source "lib/Kconfig" - -############################################################################# diff --git a/arch/v850/Kconfig.debug b/arch/v850/Kconfig.debug deleted file mode 100644 index 4acfb9cca1ca..000000000000 --- a/arch/v850/Kconfig.debug +++ /dev/null @@ -1,10 +0,0 @@ -menu "Kernel hacking" - -source "lib/Kconfig.debug" - -config NO_KERNEL_MSG - bool "Suppress Kernel BUG Messages" - help - Do not output any debug BUG messages within the kernel. - -endmenu diff --git a/arch/v850/Makefile b/arch/v850/Makefile deleted file mode 100644 index 8b629df0029a..000000000000 --- a/arch/v850/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# -# arch/v850/Makefile -# -# Copyright (C) 2001,02,03,05 NEC Corporation -# Copyright (C) 2001,02,03,05 Miles Bader -# -# This file is included by the global makefile so that you can add your own -# architecture-specific flags and dependencies. Remember to do have actions -# for "archclean" and "archdep" for cleaning up and making dependencies for -# this architecture -# -# 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 -# for more details. -# - -arch_dir = arch/v850 - -KBUILD_CFLAGS += -mv850e -# r16 is a fixed pointer to the current task -KBUILD_CFLAGS += -ffixed-r16 -mno-prolog-function -KBUILD_CFLAGS += -fno-builtin -KBUILD_CFLAGS += -D__linux__ -DUTS_SYSNAME=\"uClinux\" - -# By default, build a kernel that runs on the gdb v850 simulator. -KBUILD_DEFCONFIG := sim_defconfig - -# This prevents the linker from consolidating the .gnu.linkonce.this_module -# section into .text (which the v850 default linker script for -r does for -# some reason) -LDFLAGS_MODULE += --unique=.gnu.linkonce.this_module - -OBJCOPY_FLAGS_BLOB := -I binary -O elf32-little -B v850e - - -head-y := $(arch_dir)/kernel/head.o $(arch_dir)/kernel/init_task.o -core-y += $(arch_dir)/kernel/ -libs-y += $(arch_dir)/lib/ - - -# Deal with the initial contents of the root device -ifdef ROOT_FS_IMAGE -core-y += root_fs_image.o - -# Because the kernel build-system erases all explicit .o build rules, we -# have to use an intermediate target to fool it into building for us. -# This results in it being built anew each time, but that's alright. -root_fs_image.o: root_fs_image_force - -root_fs_image_force: $(ROOT_FS_IMAGE) - $(OBJCOPY) $(OBJCOPY_FLAGS_BLOB) --rename-section .data=.root,alloc,load,readonly,data,contents $< root_fs_image.o -endif - -CLEAN_FILES += root_fs_image.o diff --git a/arch/v850/README b/arch/v850/README deleted file mode 100644 index 12f7f7a665e0..000000000000 --- a/arch/v850/README +++ /dev/null @@ -1,44 +0,0 @@ -This port to the NEC V850E processor supports the following platforms: - - "sim" - The gdb v850e simulator (CONFIG_V850E_SIM). - - "rte-ma1-cb" - The Midas labs RTE-V850E/MA1-CB and RTE-V850E/NB85E-CB evaluation - boards (CONFIG_RTE_CB_MA1 and CONFIG_RTE_CB_NB85E). This support - has only been tested when running with the Multi-debugger monitor - ROM (for the Green Hills Multi debugger). The optional NEC - Solution Gear RTE-MOTHER-A motherboard is also supported, which - allows PCI boards to be used (CONFIG_RTE_MB_A_PCI). - - "rte-me2-cb" - The Midas labs RTE-V850E/ME2-CB evaluation board (CONFIG_RTE_CB_ME2). - This has only been tested using a kernel downloaded via an ICE - connection using the Multi debugger. Support for the RTE-MOTHER-A is - present, but hasn't been tested (unlike the other Midas labs cpu - boards, the RTE-V850E/ME2-CB includes an ethernet adaptor). - - "as85ep1" - The NEC AS85EP1 V850E evaluation chip/board (CONFIG_V850E_AS85EP1). - - "anna" - The NEC `Anna' (board/chip) implementation of the V850E2 processor - (CONFIG_V850E2_ANNA). - - "sim85e2c", "sim85e2s" - The sim85e2c and sim85e2s simulators, which are verilog simulations - of the V850E2 NA85E2C/NA85E2S cpu cores (CONFIG_V850E2_SIM85E2C and - CONFIG_V850E2_SIM85E2S). - - "fpga85e2c" - A FPGA implementation of the V850E2 NA85E2C cpu core - (CONFIG_V850E2_FPGA85E2C). - -To get a default kernel configuration for a particular platform, you can -use a _defconfig make target (e.g., "make rte-me2-cb_defconfig"); -to see which default configurations are possible, look in the directory -"arch/v850/configs". - -Porting to anything with a V850E/MA1 or MA2 processor should be simple. -See the file and the files it includes for an example of -how to add platform/chip-specific support. diff --git a/arch/v850/configs/rte-ma1-cb_defconfig b/arch/v850/configs/rte-ma1-cb_defconfig deleted file mode 100644 index 1a5beda36e29..000000000000 --- a/arch/v850/configs/rte-ma1-cb_defconfig +++ /dev/null @@ -1,617 +0,0 @@ -# -# Automatically generated make config: don't edit -# Linux kernel version: 2.6.13-uc0 -# Fri Sep 2 13:54:27 2005 -# -# CONFIG_MMU is not set -# CONFIG_UID16 is not set -CONFIG_RWSEM_GENERIC_SPINLOCK=y -# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set -CONFIG_GENERIC_CALIBRATE_DELAY=y -# CONFIG_ISA is not set -# CONFIG_ISAPNP is not set -# CONFIG_EISA is not set -# CONFIG_MCA is not set -CONFIG_V850=y - -# -# Processor type and features -# -# CONFIG_V850E_SIM is not set -CONFIG_RTE_CB_MA1=y -# CONFIG_RTE_CB_NB85E is not set -# CONFIG_RTE_CB_ME2 is not set -# CONFIG_V850E_AS85EP1 is not set -# CONFIG_V850E2_SIM85E2C is not set -# CONFIG_V850E2_SIM85E2S is not set -# CONFIG_V850E2_FPGA85E2C is not set -# CONFIG_V850E2_ANNA is not set -CONFIG_V850E=y -CONFIG_V850E_MA1=y -CONFIG_RTE_CB=y -CONFIG_RTE_CB_MULTI=y -CONFIG_RTE_CB_MULTI_DBTRAP=y -# CONFIG_RTE_CB_MA1_KSRAM is not set -CONFIG_RTE_MB_A_PCI=y -CONFIG_RTE_GBUS_INT=y -CONFIG_PCI=y -CONFIG_V850E_INTC=y -CONFIG_V850E_TIMER_D=y -# CONFIG_V850E_CACHE is not set -# CONFIG_V850E2_CACHE is not set -CONFIG_NO_CACHE=y -CONFIG_ZERO_BSS=y -# CONFIG_V850E_HIGHRES_TIMER is not set -# CONFIG_RESET_GUARD is not set -CONFIG_LARGE_ALLOCS=y -CONFIG_FLATMEM=y -CONFIG_FLAT_NODE_MEM_MAP=y - -# -# Code maturity level options -# -# CONFIG_EXPERIMENTAL is not set -CONFIG_CLEAN_COMPILE=y -CONFIG_BROKEN_ON_SMP=y -CONFIG_INIT_ENV_ARG_LIMIT=32 - -# -# General setup -# -CONFIG_LOCALVERSION="" -# CONFIG_BSD_PROCESS_ACCT is not set -# CONFIG_SYSCTL is not set -# CONFIG_AUDIT is not set -# CONFIG_HOTPLUG is not set -CONFIG_KOBJECT_UEVENT=y -# CONFIG_IKCONFIG is not set -CONFIG_EMBEDDED=y -# CONFIG_KALLSYMS is not set -CONFIG_PRINTK=y -CONFIG_BUG=y -# CONFIG_BASE_FULL is not set -# CONFIG_FUTEX is not set -# CONFIG_EPOLL is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 -CONFIG_BASE_SMALL=1 - -# -# Loadable module support -# -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_OBSOLETE_MODPARM=y -# CONFIG_MODULE_SRCVERSION_ALL is not set -CONFIG_KMOD=y - -# -# Bus options (PCI, PCMCIA, EISA, MCA, ISA) -# -# CONFIG_PCI_LEGACY_PROC is not set -# CONFIG_PCI_NAMES is not set -# CONFIG_PCI_DEBUG is not set - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set - -# -# PCI Hotplug Support -# - -# -# Executable file formats -# -CONFIG_BINFMT_FLAT=y -# CONFIG_BINFMT_ZFLAT is not set -# CONFIG_BINFMT_SHARED_FLAT is not set -# CONFIG_BINFMT_MISC is not set - -# -# Networking -# -CONFIG_NET=y - -# -# Networking options -# -# CONFIG_PACKET is not set -# CONFIG_UNIX is not set -# CONFIG_NET_KEY is not set -CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set -# CONFIG_IP_ADVANCED_ROUTER is not set -CONFIG_IP_FIB_HASH=y -# CONFIG_IP_PNP is not set -# CONFIG_NET_IPIP is not set -# CONFIG_NET_IPGRE is not set -# CONFIG_SYN_COOKIES is not set -# CONFIG_INET_AH is not set -# CONFIG_INET_ESP is not set -# CONFIG_INET_IPCOMP is not set -# CONFIG_INET_TUNNEL is not set -# CONFIG_IP_TCPDIAG is not set -# CONFIG_IP_TCPDIAG_IPV6 is not set -# CONFIG_TCP_CONG_ADVANCED is not set -CONFIG_TCP_CONG_BIC=y -# CONFIG_IPV6 is not set -# CONFIG_NETFILTER is not set -# CONFIG_BRIDGE is not set -# CONFIG_VLAN_8021Q is not set -# CONFIG_DECNET is not set -# CONFIG_LLC2 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_NET_SCHED is not set -# CONFIG_NET_CLS_ROUTE is not set - -# -# Network testing -# -# CONFIG_NET_PKTGEN is not set -# CONFIG_HAMRADIO is not set -# CONFIG_IRDA is not set -# CONFIG_BT is not set - -# -# Generic Driver Options -# -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set -# CONFIG_DEBUG_DRIVER is not set - -# -# Memory Technology Devices (MTD) -# -CONFIG_MTD=y -# CONFIG_MTD_DEBUG is not set -# CONFIG_MTD_CONCAT is not set -# CONFIG_MTD_PARTITIONS is not set - -# -# User Modules And Translation Layers -# -# CONFIG_MTD_CHAR is not set -CONFIG_MTD_BLOCK=y -# CONFIG_FTL is not set -# CONFIG_NFTL is not set -# CONFIG_INFTL is not set - -# -# RAM/ROM/Flash chip drivers -# -# CONFIG_MTD_CFI is not set -# CONFIG_MTD_JEDECPROBE is not set -CONFIG_MTD_MAP_BANK_WIDTH_1=y -CONFIG_MTD_MAP_BANK_WIDTH_2=y -CONFIG_MTD_MAP_BANK_WIDTH_4=y -# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set -CONFIG_MTD_CFI_I1=y -CONFIG_MTD_CFI_I2=y -# CONFIG_MTD_CFI_I4 is not set -# CONFIG_MTD_CFI_I8 is not set -# CONFIG_MTD_RAM is not set -# CONFIG_MTD_ROM is not set -# CONFIG_MTD_ABSENT is not set - -# -# Mapping drivers for chip access -# -# CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PLATRAM is not set - -# -# Self-contained MTD device drivers -# -# CONFIG_MTD_PMC551 is not set -CONFIG_MTD_SLRAM=y -# CONFIG_MTD_PHRAM is not set -# CONFIG_MTD_MTDRAM is not set -# CONFIG_MTD_BLKMTD is not set - -# -# Disk-On-Chip Device Drivers -# -# CONFIG_MTD_DOC2000 is not set -# CONFIG_MTD_DOC2001 is not set -# CONFIG_MTD_DOC2001PLUS is not set - -# -# NAND Flash Device Drivers -# -# CONFIG_MTD_NAND is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Block devices -# -# CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_CPQ_DA is not set -# CONFIG_BLK_CPQ_CISS_DA is not set -# CONFIG_BLK_DEV_DAC960 is not set -# CONFIG_BLK_DEV_COW_COMMON is not set -# CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_SX8 is not set -# CONFIG_BLK_DEV_RAM is not set -CONFIG_BLK_DEV_RAM_COUNT=16 -CONFIG_INITRAMFS_SOURCE="" -# CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -# CONFIG_IOSCHED_DEADLINE is not set -# CONFIG_IOSCHED_CFQ is not set -# CONFIG_ATA_OVER_ETH is not set - -# -# Disk device support -# - -# -# ATA/ATAPI/MFM/RLL support -# -# CONFIG_IDE is not set - -# -# SCSI device support -# -# CONFIG_SCSI is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# Fusion MPT device support -# -# CONFIG_FUSION is not set - -# -# IEEE 1394 (FireWire) support -# -# CONFIG_IEEE1394 is not set - -# -# I2O device support -# -# CONFIG_I2O is not set - -# -# Network device support -# -CONFIG_NETDEVICES=y -# CONFIG_DUMMY is not set -# CONFIG_BONDING is not set -# CONFIG_EQUALIZER is not set -# CONFIG_TUN is not set - -# -# ARCnet devices -# -# CONFIG_ARCNET is not set - -# -# Ethernet (10 or 100Mbit) -# -CONFIG_NET_ETHERNET=y -CONFIG_MII=y -# CONFIG_HAPPYMEAL is not set -# CONFIG_SUNGEM is not set -# CONFIG_NET_VENDOR_3COM is not set -# CONFIG_NET_VENDOR_SMC is not set - -# -# Tulip family network device support -# -# CONFIG_NET_TULIP is not set -# CONFIG_HP100 is not set -# CONFIG_NE2000 is not set -CONFIG_NET_PCI=y -# CONFIG_PCNET32 is not set -# CONFIG_AMD8111_ETH is not set -# CONFIG_ADAPTEC_STARFIRE is not set -# CONFIG_DGRS is not set -CONFIG_EEPRO100=y -# CONFIG_E100 is not set -# CONFIG_FEALNX is not set -# CONFIG_NATSEMI is not set -# CONFIG_NE2K_PCI is not set -# CONFIG_8139TOO is not set -# CONFIG_SIS900 is not set -# CONFIG_EPIC100 is not set -# CONFIG_SUNDANCE is not set -# CONFIG_TLAN is not set -# CONFIG_VIA_RHINE is not set - -# -# Ethernet (1000 Mbit) -# -# CONFIG_ACENIC is not set -# CONFIG_DL2K is not set -# CONFIG_E1000 is not set -# CONFIG_NS83820 is not set -# CONFIG_HAMACHI is not set -# CONFIG_R8169 is not set -# CONFIG_SK98LIN is not set -# CONFIG_VIA_VELOCITY is not set -# CONFIG_TIGON3 is not set -# CONFIG_BNX2 is not set - -# -# Ethernet (10000 Mbit) -# -# CONFIG_IXGB is not set -# CONFIG_S2IO is not set - -# -# Token Ring devices -# -# CONFIG_TR is not set - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set - -# -# Wan interfaces -# -# CONFIG_WAN is not set -# CONFIG_FDDI is not set -# CONFIG_PPP is not set -# CONFIG_SLIP is not set -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set - -# -# ISDN subsystem -# -# CONFIG_ISDN is not set - -# -# Input device support -# -CONFIG_INPUT=y - -# -# Userland interfaces -# -# CONFIG_INPUT_MOUSEDEV is not set -# CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set -# CONFIG_INPUT_EVDEV is not set -# CONFIG_INPUT_EVBUG is not set - -# -# Input Device Drivers -# -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# -# Hardware I/O ports -# -# CONFIG_SERIO is not set -# CONFIG_GAMEPORT is not set - -# -# Character devices -# -# CONFIG_VT is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -# CONFIG_SERIAL_8250 is not set - -# -# Non-8250 serial port support -# -CONFIG_V850E_UART=y -CONFIG_V850E_UART_CONSOLE=y -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y -# CONFIG_SERIAL_JSM is not set -# CONFIG_UNIX98_PTYS is not set -# CONFIG_LEGACY_PTYS is not set - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -# CONFIG_WATCHDOG is not set -# CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set -# CONFIG_DTLK is not set -# CONFIG_R3964 is not set -# CONFIG_APPLICOM is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_DRM is not set -# CONFIG_RAW_DRIVER is not set - -# -# TPM devices -# - -# -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set - -# -# Digital Video Broadcasting Devices -# -# CONFIG_DVB is not set - -# -# File systems -# -# CONFIG_EXT2_FS is not set -# CONFIG_EXT3_FS is not set -# CONFIG_JBD is not set -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set - -# -# XFS support -# -# CONFIG_XFS_FS is not set -# CONFIG_MINIX_FS is not set -CONFIG_ROMFS_FS=y -# CONFIG_MAGIC_ROM_PTR is not set -CONFIG_INOTIFY=y -# CONFIG_QUOTA is not set -CONFIG_DNOTIFY=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_AUTOFS4_FS is not set - -# -# CD-ROM/DVD Filesystems -# -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set - -# -# DOS/FAT/NT Filesystems -# -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set -# CONFIG_NTFS_FS is not set - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -CONFIG_SYSFS=y -# CONFIG_TMPFS is not set -# CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y - -# -# Miscellaneous filesystems -# -# CONFIG_HFSPLUS_FS is not set -# CONFIG_JFFS_FS is not set -# CONFIG_JFFS2_FS is not set -# CONFIG_CRAMFS is not set -# CONFIG_VXFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set - -# -# Network File Systems -# -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -# CONFIG_NFS_V3_ACL is not set -# CONFIG_NFSD is not set -CONFIG_LOCKD=y -CONFIG_LOCKD_V4=y -CONFIG_NFS_COMMON=y -CONFIG_SUNRPC=y -# CONFIG_SMB_FS is not set -# CONFIG_CIFS is not set -# CONFIG_NCP_FS is not set -# CONFIG_CODA_FS is not set - -# -# Partition Types -# -# CONFIG_PARTITION_ADVANCED is not set -CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# -# CONFIG_NLS is not set - -# -# Graphics support -# -# CONFIG_FB is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# USB support -# -CONFIG_USB_ARCH_HAS_HCD=y -CONFIG_USB_ARCH_HAS_OHCI=y -# CONFIG_USB is not set - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set - -# -# Kernel hacking -# -# CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y -# CONFIG_MAGIC_SYSRQ is not set -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_SCHEDSTATS is not set -# CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_SPINLOCK_SLEEP is not set -# CONFIG_DEBUG_KOBJECT is not set -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_FS is not set -# CONFIG_NO_KERNEL_MSG is not set - -# -# Security options -# -# CONFIG_KEYS is not set -# CONFIG_SECURITY is not set - -# -# Cryptographic options -# -# CONFIG_CRYPTO is not set - -# -# Hardware crypto devices -# - -# -# Library routines -# -# CONFIG_CRC_CCITT is not set -# CONFIG_CRC32 is not set -# CONFIG_LIBCRC32C is not set diff --git a/arch/v850/configs/rte-me2-cb_defconfig b/arch/v850/configs/rte-me2-cb_defconfig deleted file mode 100644 index 15e666478061..000000000000 --- a/arch/v850/configs/rte-me2-cb_defconfig +++ /dev/null @@ -1,462 +0,0 @@ -# -# Automatically generated make config: don't edit -# Linux kernel version: 2.6.13-uc0 -# Fri Sep 2 13:47:50 2005 -# -# CONFIG_MMU is not set -# CONFIG_UID16 is not set -CONFIG_RWSEM_GENERIC_SPINLOCK=y -# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set -CONFIG_GENERIC_CALIBRATE_DELAY=y -# CONFIG_ISA is not set -# CONFIG_ISAPNP is not set -# CONFIG_EISA is not set -# CONFIG_MCA is not set -CONFIG_V850=y - -# -# Processor type and features -# -# CONFIG_V850E_SIM is not set -# CONFIG_RTE_CB_MA1 is not set -# CONFIG_RTE_CB_NB85E is not set -CONFIG_RTE_CB_ME2=y -# CONFIG_V850E_AS85EP1 is not set -# CONFIG_V850E2_SIM85E2C is not set -# CONFIG_V850E2_SIM85E2S is not set -# CONFIG_V850E2_FPGA85E2C is not set -# CONFIG_V850E2_ANNA is not set -CONFIG_V850E=y -CONFIG_V850E_ME2=y -CONFIG_RTE_CB=y -# CONFIG_RTE_MB_A_PCI is not set -# CONFIG_PCI is not set -CONFIG_V850E_INTC=y -CONFIG_V850E_TIMER_D=y -CONFIG_V850E_CACHE=y -# CONFIG_V850E2_CACHE is not set -# CONFIG_NO_CACHE is not set -# CONFIG_ROM_KERNEL is not set -CONFIG_ZERO_BSS=y -# CONFIG_V850E_HIGHRES_TIMER is not set -# CONFIG_RESET_GUARD is not set -CONFIG_LARGE_ALLOCS=y -CONFIG_FLATMEM=y -CONFIG_FLAT_NODE_MEM_MAP=y - -# -# Code maturity level options -# -# CONFIG_EXPERIMENTAL is not set -CONFIG_CLEAN_COMPILE=y -CONFIG_BROKEN_ON_SMP=y -CONFIG_INIT_ENV_ARG_LIMIT=32 - -# -# General setup -# -CONFIG_LOCALVERSION="" -# CONFIG_BSD_PROCESS_ACCT is not set -# CONFIG_SYSCTL is not set -# CONFIG_HOTPLUG is not set -# CONFIG_IKCONFIG is not set -CONFIG_EMBEDDED=y -# CONFIG_KALLSYMS is not set -CONFIG_PRINTK=y -CONFIG_BUG=y -# CONFIG_BASE_FULL is not set -# CONFIG_FUTEX is not set -# CONFIG_EPOLL is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 -CONFIG_BASE_SMALL=1 - -# -# Loadable module support -# -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_OBSOLETE_MODPARM=y -# CONFIG_MODULE_SRCVERSION_ALL is not set -CONFIG_KMOD=y - -# -# Bus options (PCI, PCMCIA, EISA, MCA, ISA) -# - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set - -# -# PCI Hotplug Support -# - -# -# Executable file formats -# -CONFIG_BINFMT_FLAT=y -# CONFIG_BINFMT_ZFLAT is not set -# CONFIG_BINFMT_SHARED_FLAT is not set -# CONFIG_BINFMT_MISC is not set - -# -# Networking -# -# CONFIG_NET is not set - -# -# Generic Driver Options -# -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set -# CONFIG_DEBUG_DRIVER is not set - -# -# Memory Technology Devices (MTD) -# -CONFIG_MTD=y -# CONFIG_MTD_DEBUG is not set -# CONFIG_MTD_CONCAT is not set -# CONFIG_MTD_PARTITIONS is not set - -# -# User Modules And Translation Layers -# -# CONFIG_MTD_CHAR is not set -CONFIG_MTD_BLOCK=y -# CONFIG_FTL is not set -# CONFIG_NFTL is not set -# CONFIG_INFTL is not set - -# -# RAM/ROM/Flash chip drivers -# -# CONFIG_MTD_CFI is not set -# CONFIG_MTD_JEDECPROBE is not set -CONFIG_MTD_MAP_BANK_WIDTH_1=y -CONFIG_MTD_MAP_BANK_WIDTH_2=y -CONFIG_MTD_MAP_BANK_WIDTH_4=y -# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set -CONFIG_MTD_CFI_I1=y -CONFIG_MTD_CFI_I2=y -# CONFIG_MTD_CFI_I4 is not set -# CONFIG_MTD_CFI_I8 is not set -# CONFIG_MTD_RAM is not set -# CONFIG_MTD_ROM is not set -# CONFIG_MTD_ABSENT is not set - -# -# Mapping drivers for chip access -# -# CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PLATRAM is not set - -# -# Self-contained MTD device drivers -# -CONFIG_MTD_SLRAM=y -# CONFIG_MTD_PHRAM is not set -# CONFIG_MTD_MTDRAM is not set -# CONFIG_MTD_BLKMTD is not set - -# -# Disk-On-Chip Device Drivers -# -# CONFIG_MTD_DOC2000 is not set -# CONFIG_MTD_DOC2001 is not set -# CONFIG_MTD_DOC2001PLUS is not set - -# -# NAND Flash Device Drivers -# -# CONFIG_MTD_NAND is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Block devices -# -# CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_DEV_COW_COMMON is not set -# CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_RAM is not set -CONFIG_BLK_DEV_RAM_COUNT=16 -CONFIG_INITRAMFS_SOURCE="" -# CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -# CONFIG_IOSCHED_DEADLINE is not set -# CONFIG_IOSCHED_CFQ is not set - -# -# Disk device support -# - -# -# ATA/ATAPI/MFM/RLL support -# -# CONFIG_IDE is not set - -# -# SCSI device support -# -# CONFIG_SCSI is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# Fusion MPT device support -# -# CONFIG_FUSION is not set - -# -# IEEE 1394 (FireWire) support -# - -# -# I2O device support -# - -# -# Network device support -# -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set - -# -# ISDN subsystem -# - -# -# Input device support -# -CONFIG_INPUT=y - -# -# Userland interfaces -# -# CONFIG_INPUT_MOUSEDEV is not set -# CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set -# CONFIG_INPUT_EVDEV is not set -# CONFIG_INPUT_EVBUG is not set - -# -# Input Device Drivers -# -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# -# Hardware I/O ports -# -CONFIG_SERIO=y -# CONFIG_SERIO_I8042 is not set -# CONFIG_SERIO_SERPORT is not set -# CONFIG_SERIO_LIBPS2 is not set -# CONFIG_SERIO_RAW is not set -# CONFIG_GAMEPORT is not set - -# -# Character devices -# -# CONFIG_VT is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_NR_UARTS=1 -# CONFIG_SERIAL_8250_EXTENDED is not set - -# -# Non-8250 serial port support -# -# CONFIG_V850E_UART is not set -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y -# CONFIG_UNIX98_PTYS is not set -# CONFIG_LEGACY_PTYS is not set - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -# CONFIG_WATCHDOG is not set -# CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set -# CONFIG_DTLK is not set -# CONFIG_R3964 is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_RAW_DRIVER is not set - -# -# TPM devices -# - -# -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set - -# -# Digital Video Broadcasting Devices -# - -# -# File systems -# -# CONFIG_EXT2_FS is not set -# CONFIG_EXT3_FS is not set -# CONFIG_JBD is not set -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set - -# -# XFS support -# -# CONFIG_XFS_FS is not set -# CONFIG_MINIX_FS is not set -CONFIG_ROMFS_FS=y -# CONFIG_MAGIC_ROM_PTR is not set -CONFIG_INOTIFY=y -# CONFIG_QUOTA is not set -CONFIG_DNOTIFY=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_AUTOFS4_FS is not set - -# -# CD-ROM/DVD Filesystems -# -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set - -# -# DOS/FAT/NT Filesystems -# -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set -# CONFIG_NTFS_FS is not set - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -CONFIG_SYSFS=y -# CONFIG_TMPFS is not set -# CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y - -# -# Miscellaneous filesystems -# -# CONFIG_HFSPLUS_FS is not set -# CONFIG_JFFS_FS is not set -# CONFIG_JFFS2_FS is not set -# CONFIG_CRAMFS is not set -# CONFIG_VXFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set - -# -# Partition Types -# -# CONFIG_PARTITION_ADVANCED is not set -CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# -# CONFIG_NLS is not set - -# -# Graphics support -# -# CONFIG_FB is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# USB support -# -# CONFIG_USB_ARCH_HAS_HCD is not set -# CONFIG_USB_ARCH_HAS_OHCI is not set - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set - -# -# Kernel hacking -# -# CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y -# CONFIG_MAGIC_SYSRQ is not set -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_SCHEDSTATS is not set -# CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_SPINLOCK_SLEEP is not set -# CONFIG_DEBUG_KOBJECT is not set -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_FS is not set -# CONFIG_NO_KERNEL_MSG is not set - -# -# Security options -# -# CONFIG_KEYS is not set -# CONFIG_SECURITY is not set - -# -# Cryptographic options -# -# CONFIG_CRYPTO is not set - -# -# Hardware crypto devices -# - -# -# Library routines -# -# CONFIG_CRC_CCITT is not set -# CONFIG_CRC32 is not set -# CONFIG_LIBCRC32C is not set diff --git a/arch/v850/configs/sim_defconfig b/arch/v850/configs/sim_defconfig deleted file mode 100644 index f31ba7398ad0..000000000000 --- a/arch/v850/configs/sim_defconfig +++ /dev/null @@ -1,451 +0,0 @@ -# -# Automatically generated make config: don't edit -# Linux kernel version: 2.6.13-uc0 -# Fri Sep 2 13:36:43 2005 -# -# CONFIG_MMU is not set -# CONFIG_UID16 is not set -CONFIG_RWSEM_GENERIC_SPINLOCK=y -# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set -CONFIG_GENERIC_CALIBRATE_DELAY=y -# CONFIG_ISA is not set -# CONFIG_ISAPNP is not set -# CONFIG_EISA is not set -# CONFIG_MCA is not set -CONFIG_V850=y - -# -# Processor type and features -# -CONFIG_V850E_SIM=y -# CONFIG_RTE_CB_MA1 is not set -# CONFIG_RTE_CB_NB85E is not set -# CONFIG_RTE_CB_ME2 is not set -# CONFIG_V850E_AS85EP1 is not set -# CONFIG_V850E2_SIM85E2C is not set -# CONFIG_V850E2_SIM85E2S is not set -# CONFIG_V850E2_FPGA85E2C is not set -# CONFIG_V850E2_ANNA is not set -CONFIG_V850E=y -# CONFIG_PCI is not set -# CONFIG_V850E_INTC is not set -# CONFIG_V850E_TIMER_D is not set -# CONFIG_V850E_CACHE is not set -# CONFIG_V850E2_CACHE is not set -CONFIG_NO_CACHE=y -CONFIG_ZERO_BSS=y -# CONFIG_RESET_GUARD is not set -CONFIG_LARGE_ALLOCS=y -CONFIG_FLATMEM=y -CONFIG_FLAT_NODE_MEM_MAP=y - -# -# Code maturity level options -# -# CONFIG_EXPERIMENTAL is not set -CONFIG_CLEAN_COMPILE=y -CONFIG_BROKEN_ON_SMP=y -CONFIG_INIT_ENV_ARG_LIMIT=32 - -# -# General setup -# -CONFIG_LOCALVERSION="" -# CONFIG_BSD_PROCESS_ACCT is not set -# CONFIG_SYSCTL is not set -# CONFIG_HOTPLUG is not set -# CONFIG_IKCONFIG is not set -CONFIG_EMBEDDED=y -# CONFIG_KALLSYMS is not set -CONFIG_PRINTK=y -CONFIG_BUG=y -# CONFIG_BASE_FULL is not set -# CONFIG_FUTEX is not set -# CONFIG_EPOLL is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 -CONFIG_BASE_SMALL=1 - -# -# Loadable module support -# -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_OBSOLETE_MODPARM=y -# CONFIG_MODULE_SRCVERSION_ALL is not set -CONFIG_KMOD=y - -# -# Bus options (PCI, PCMCIA, EISA, MCA, ISA) -# - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set - -# -# PCI Hotplug Support -# - -# -# Executable file formats -# -CONFIG_BINFMT_FLAT=y -# CONFIG_BINFMT_ZFLAT is not set -# CONFIG_BINFMT_SHARED_FLAT is not set -# CONFIG_BINFMT_MISC is not set - -# -# Networking -# -# CONFIG_NET is not set - -# -# Generic Driver Options -# -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set -# CONFIG_DEBUG_DRIVER is not set - -# -# Memory Technology Devices (MTD) -# -CONFIG_MTD=y -# CONFIG_MTD_DEBUG is not set -# CONFIG_MTD_CONCAT is not set -# CONFIG_MTD_PARTITIONS is not set - -# -# User Modules And Translation Layers -# -# CONFIG_MTD_CHAR is not set -CONFIG_MTD_BLOCK=y -# CONFIG_FTL is not set -# CONFIG_NFTL is not set -# CONFIG_INFTL is not set - -# -# RAM/ROM/Flash chip drivers -# -# CONFIG_MTD_CFI is not set -# CONFIG_MTD_JEDECPROBE is not set -CONFIG_MTD_MAP_BANK_WIDTH_1=y -CONFIG_MTD_MAP_BANK_WIDTH_2=y -CONFIG_MTD_MAP_BANK_WIDTH_4=y -# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set -# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set -CONFIG_MTD_CFI_I1=y -CONFIG_MTD_CFI_I2=y -# CONFIG_MTD_CFI_I4 is not set -# CONFIG_MTD_CFI_I8 is not set -# CONFIG_MTD_RAM is not set -# CONFIG_MTD_ROM is not set -# CONFIG_MTD_ABSENT is not set - -# -# Mapping drivers for chip access -# -# CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PLATRAM is not set - -# -# Self-contained MTD device drivers -# -CONFIG_MTD_SLRAM=y -# CONFIG_MTD_PHRAM is not set -# CONFIG_MTD_MTDRAM is not set -# CONFIG_MTD_BLKMTD is not set - -# -# Disk-On-Chip Device Drivers -# -# CONFIG_MTD_DOC2000 is not set -# CONFIG_MTD_DOC2001 is not set -# CONFIG_MTD_DOC2001PLUS is not set - -# -# NAND Flash Device Drivers -# -# CONFIG_MTD_NAND is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Block devices -# -# CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_DEV_COW_COMMON is not set -# CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_RAM is not set -CONFIG_BLK_DEV_RAM_COUNT=16 -CONFIG_INITRAMFS_SOURCE="" -# CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -# CONFIG_IOSCHED_DEADLINE is not set -# CONFIG_IOSCHED_CFQ is not set - -# -# Disk device support -# - -# -# ATA/ATAPI/MFM/RLL support -# -# CONFIG_IDE is not set - -# -# SCSI device support -# -# CONFIG_SCSI is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# Fusion MPT device support -# -# CONFIG_FUSION is not set - -# -# IEEE 1394 (FireWire) support -# - -# -# I2O device support -# - -# -# Network device support -# -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set - -# -# ISDN subsystem -# - -# -# Input device support -# -CONFIG_INPUT=y - -# -# Userland interfaces -# -# CONFIG_INPUT_MOUSEDEV is not set -# CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set -# CONFIG_INPUT_EVDEV is not set -# CONFIG_INPUT_EVBUG is not set - -# -# Input Device Drivers -# -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# -# Hardware I/O ports -# -CONFIG_SERIO=y -# CONFIG_SERIO_I8042 is not set -# CONFIG_SERIO_SERPORT is not set -# CONFIG_SERIO_LIBPS2 is not set -# CONFIG_SERIO_RAW is not set -# CONFIG_GAMEPORT is not set - -# -# Character devices -# -# CONFIG_VT is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -# CONFIG_SERIAL_8250 is not set - -# -# Non-8250 serial port support -# -# CONFIG_UNIX98_PTYS is not set -# CONFIG_LEGACY_PTYS is not set - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -# CONFIG_WATCHDOG is not set -# CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set -# CONFIG_DTLK is not set -# CONFIG_R3964 is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_RAW_DRIVER is not set - -# -# TPM devices -# - -# -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set - -# -# Digital Video Broadcasting Devices -# - -# -# File systems -# -# CONFIG_EXT2_FS is not set -# CONFIG_EXT3_FS is not set -# CONFIG_JBD is not set -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set - -# -# XFS support -# -# CONFIG_XFS_FS is not set -# CONFIG_MINIX_FS is not set -CONFIG_ROMFS_FS=y -# CONFIG_MAGIC_ROM_PTR is not set -CONFIG_INOTIFY=y -# CONFIG_QUOTA is not set -CONFIG_DNOTIFY=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_AUTOFS4_FS is not set - -# -# CD-ROM/DVD Filesystems -# -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set - -# -# DOS/FAT/NT Filesystems -# -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set -# CONFIG_NTFS_FS is not set - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -CONFIG_SYSFS=y -# CONFIG_TMPFS is not set -# CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y - -# -# Miscellaneous filesystems -# -# CONFIG_HFSPLUS_FS is not set -# CONFIG_JFFS_FS is not set -# CONFIG_JFFS2_FS is not set -# CONFIG_CRAMFS is not set -# CONFIG_VXFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set - -# -# Partition Types -# -# CONFIG_PARTITION_ADVANCED is not set -CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# -# CONFIG_NLS is not set - -# -# Graphics support -# -# CONFIG_FB is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# USB support -# -# CONFIG_USB_ARCH_HAS_HCD is not set -# CONFIG_USB_ARCH_HAS_OHCI is not set - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set - -# -# Kernel hacking -# -# CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y -# CONFIG_MAGIC_SYSRQ is not set -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_SCHEDSTATS is not set -# CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_SPINLOCK_SLEEP is not set -# CONFIG_DEBUG_KOBJECT is not set -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_FS is not set -# CONFIG_NO_KERNEL_MSG is not set - -# -# Security options -# -# CONFIG_KEYS is not set -# CONFIG_SECURITY is not set - -# -# Cryptographic options -# -# CONFIG_CRYPTO is not set - -# -# Hardware crypto devices -# - -# -# Library routines -# -# CONFIG_CRC_CCITT is not set -# CONFIG_CRC32 is not set -# CONFIG_LIBCRC32C is not set diff --git a/arch/v850/kernel/Makefile b/arch/v850/kernel/Makefile deleted file mode 100644 index da5889c53576..000000000000 --- a/arch/v850/kernel/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -# -# arch/v850/kernel/Makefile -# -# Copyright (C) 2001,02,03 NEC Electronics Corporation -# Copyright (C) 2001,02,03 Miles Bader -# -# 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 -# for more details. -# - -extra-y := head.o init_task.o vmlinux.lds - -obj-y += intv.o entry.o process.o syscalls.o time.o setup.o \ - signal.o irq.o mach.o ptrace.o bug.o -obj-$(CONFIG_MODULES) += module.o v850_ksyms.o -# chip-specific code -obj-$(CONFIG_V850E_MA1) += ma.o -obj-$(CONFIG_V850E_ME2) += me2.o -obj-$(CONFIG_V850E_TEG) += teg.o -obj-$(CONFIG_V850E_AS85EP1) += as85ep1.o -obj-$(CONFIG_V850E2_ANNA) += anna.o -# platform-specific code -obj-$(CONFIG_V850E_SIM) += sim.o simcons.o -obj-$(CONFIG_V850E2_SIM85E2) += sim85e2.o memcons.o -obj-$(CONFIG_V850E2_FPGA85E2C) += fpga85e2c.o memcons.o -obj-$(CONFIG_RTE_CB) += rte_cb.o rte_cb_leds.o -obj-$(CONFIG_RTE_CB_MA1) += rte_ma1_cb.o -obj-$(CONFIG_RTE_CB_ME2) += rte_me2_cb.o -obj-$(CONFIG_RTE_CB_NB85E) += rte_nb85e_cb.o -obj-$(CONFIG_RTE_CB_MULTI) += rte_cb_multi.o -obj-$(CONFIG_RTE_MB_A_PCI) += rte_mb_a_pci.o -obj-$(CONFIG_RTE_GBUS_INT) += gbus_int.o -# feature-specific code -obj-$(CONFIG_V850E_INTC) += v850e_intc.o -obj-$(CONFIG_V850E_TIMER_D) += v850e_timer_d.o v850e_utils.o -obj-$(CONFIG_V850E_CACHE) += v850e_cache.o -obj-$(CONFIG_V850E2_CACHE) += v850e2_cache.o -obj-$(CONFIG_V850E_HIGHRES_TIMER) += highres_timer.o -obj-$(CONFIG_PROC_FS) += procfs.o diff --git a/arch/v850/kernel/anna-rom.ld b/arch/v850/kernel/anna-rom.ld deleted file mode 100644 index 7c54e7e3f1b1..000000000000 --- a/arch/v850/kernel/anna-rom.ld +++ /dev/null @@ -1,16 +0,0 @@ -/* Linker script for the Midas labs Anna V850E2 evaluation board - (CONFIG_V850E2_ANNA), with kernel in ROM (CONFIG_ROM_KERNEL). */ - -MEMORY { - /* 8MB of flash ROM. */ - ROM : ORIGIN = 0, LENGTH = 0x00800000 - - /* 1MB of static RAM. This memory is mirrored 64 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - /* 64MB of DRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - ROMK_SECTIONS(ROM, SRAM) -} diff --git a/arch/v850/kernel/anna.c b/arch/v850/kernel/anna.c deleted file mode 100644 index 5978a25170fb..000000000000 --- a/arch/v850/kernel/anna.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * arch/v850/kernel/anna.c -- Anna V850E2 evaluation chip/board - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "mach.h" - - -/* SRAM and SDRAM are vaguely contiguous (with a big hole in between; see - mach_reserve_bootmem for details); use both as one big area. */ -#define RAM_START SRAM_ADDR -#define RAM_END (SDRAM_ADDR + SDRAM_SIZE) - -/* The bits of this port are connected to an 8-LED bar-graph. */ -#define LEDS_PORT 0 - - -static void anna_led_tick (void); - - -void __init mach_early_init (void) -{ - ANNA_ILBEN = 0; - - V850E2_CSC(0) = 0x402F; - V850E2_CSC(1) = 0x4000; - V850E2_BPC = 0; - V850E2_BSC = 0xAAAA; - V850E2_BEC = 0; - -#if 0 - V850E2_BHC = 0xFFFF; /* icache all memory, dcache all */ -#else - V850E2_BHC = 0; /* cache no memory */ -#endif - V850E2_BCT(0) = 0xB088; - V850E2_BCT(1) = 0x0008; - V850E2_DWC(0) = 0x0027; - V850E2_DWC(1) = 0; - V850E2_BCC = 0x0006; - V850E2_ASC = 0; - V850E2_LBS = 0x0089; - V850E2_SCR(3) = 0x21A9; - V850E2_RFS(3) = 0x8121; - - v850e_intc_disable_irqs (); -} - -void __init mach_setup (char **cmdline) -{ - ANNA_PORT_PM (LEDS_PORT) = 0; /* Make all LED pins output pins. */ - mach_tick = anna_led_tick; -} - -void __init mach_get_physical_ram (unsigned long *ram_start, - unsigned long *ram_len) -{ - *ram_start = RAM_START; - *ram_len = RAM_END - RAM_START; -} - -void __init mach_reserve_bootmem () -{ - /* The space between SRAM and SDRAM is filled with duplicate - images of SRAM. Prevent the kernel from using them. */ - reserve_bootmem (SRAM_ADDR + SRAM_SIZE, - SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE), - BOOTMEM_DEFAULT); -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Start hardware timer. */ - v850e_timer_d_configure (0, HZ); - /* Install timer interrupt handler. */ - setup_irq (IRQ_INTCMD(0), timer_action); -} - -static struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_MACH_IRQS, 1, 7 }, - { "PIN", IRQ_INTP(0), IRQ_INTP_NUM, 1, 4 }, - { "CCC", IRQ_INTCCC(0), IRQ_INTCCC_NUM, 1, 5 }, - { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 }, - { "DMA", IRQ_INTDMA(0), IRQ_INTDMA_NUM, 1, 2 }, - { "DMXER", IRQ_INTDMXER,1, 1, 2 }, - { "SRE", IRQ_INTSRE(0), IRQ_INTSRE_NUM, 3, 3 }, - { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 3, 4 }, - { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 3, 5 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -void __init mach_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - -void machine_restart (char *__unused) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - asm ("jmp r0"); /* Jump to the reset vector. */ -} - -void machine_halt (void) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - local_irq_disable (); /* Ignore all interrupts. */ - ANNA_PORT_IO(LEDS_PORT) = 0xAA; /* Note that we halted. */ - for (;;) - asm ("halt; nop; nop; nop; nop; nop"); -} - -void machine_power_off (void) -{ - machine_halt (); -} - -/* Called before configuring an on-chip UART. */ -void anna_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud) -{ - /* The Anna connects some general-purpose I/O pins on the CPU to - the RTS/CTS lines of UART 1's serial connection. I/O pins P07 - and P37 are RTS and CTS respectively. */ - if (chan == 1) { - ANNA_PORT_PM(0) &= ~0x80; /* P07 in output mode */ - ANNA_PORT_PM(3) |= 0x80; /* P37 in input mode */ - } -} - -/* Minimum and maximum bounds for the moving upper LED boundary in the - clock tick display. We can't use the last bit because it's used for - UART0's CTS output. */ -#define MIN_MAX_POS 0 -#define MAX_MAX_POS 6 - -/* There are MAX_MAX_POS^2 - MIN_MAX_POS^2 cycles in the animation, so if - we pick 6 and 0 as above, we get 49 cycles, which is when divided into - the standard 100 value for HZ, gives us an almost 1s total time. */ -#define TICKS_PER_FRAME \ - (HZ / (MAX_MAX_POS * MAX_MAX_POS - MIN_MAX_POS * MIN_MAX_POS)) - -static void anna_led_tick () -{ - static unsigned counter = 0; - - if (++counter == TICKS_PER_FRAME) { - static int pos = 0, max_pos = MAX_MAX_POS, dir = 1; - - if (dir > 0 && pos == max_pos) { - dir = -1; - if (max_pos == MIN_MAX_POS) - max_pos = MAX_MAX_POS; - else - max_pos--; - } else { - if (dir < 0 && pos == 0) - dir = 1; - - if (pos + dir <= max_pos) { - /* Each bit of port 0 has a LED. */ - clear_bit (pos, &ANNA_PORT_IO(LEDS_PORT)); - pos += dir; - set_bit (pos, &ANNA_PORT_IO(LEDS_PORT)); - } - } - - counter = 0; - } -} diff --git a/arch/v850/kernel/anna.ld b/arch/v850/kernel/anna.ld deleted file mode 100644 index df7f80f2833d..000000000000 --- a/arch/v850/kernel/anna.ld +++ /dev/null @@ -1,20 +0,0 @@ -/* Linker script for the Midas labs Anna V850E2 evaluation board - (CONFIG_V850E2_ANNA). */ - -MEMORY { - /* 256KB of internal memory (followed by one mirror). */ - iMEM0 : ORIGIN = 0, LENGTH = 0x00040000 - /* 256KB of internal memory (followed by one mirror). */ - iMEM1 : ORIGIN = 0x00040000, LENGTH = 0x00040000 - - /* 1MB of static RAM. This memory is mirrored 64 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - /* 64MB of DRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - .intv : { INTV_CONTENTS } > iMEM0 - .sram : { RAMK_KRAM_CONTENTS } > SRAM - .root : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/as85ep1-rom.ld b/arch/v850/kernel/as85ep1-rom.ld deleted file mode 100644 index fe2a9a3ab525..000000000000 --- a/arch/v850/kernel/as85ep1-rom.ld +++ /dev/null @@ -1,21 +0,0 @@ -/* Linker script for the NEC AS85EP1 V850E evaluation board - (CONFIG_V850E_AS85EP1), with kernel in ROM (CONFIG_ROM_KERNEL). */ - -MEMORY { - /* 4MB of flash ROM. */ - ROM : ORIGIN = 0, LENGTH = 0x00400000 - - /* 1MB of static RAM. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - - /* About 58MB of DRAM. This can actually be at one of two - positions, determined by jumper JP3; we have to use the first - position because the second is partially out of processor - instruction addressing range (though in the second position - there's actually 64MB available). */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - ROMK_SECTIONS(ROM, SRAM) -} diff --git a/arch/v850/kernel/as85ep1.c b/arch/v850/kernel/as85ep1.c deleted file mode 100644 index b525ecf3aea4..000000000000 --- a/arch/v850/kernel/as85ep1.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * arch/v850/kernel/as85ep1.c -- AS85EP1 V850E evaluation chip/board - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "mach.h" - - -/* SRAM and SDRAM are vaguely contiguous (with a big hole in between; see - mach_reserve_bootmem for details); use both as one big area. */ -#define RAM_START SRAM_ADDR -#define RAM_END (SDRAM_ADDR + SDRAM_SIZE) - -/* The bits of this port are connected to an 8-LED bar-graph. */ -#define LEDS_PORT 4 - - -static void as85ep1_led_tick (void); - -extern char _intv_copy_src_start, _intv_copy_src_end; -extern char _intv_copy_dst_start; - - -void __init mach_early_init (void) -{ -#ifndef CONFIG_ROM_KERNEL - const u32 *src; - register u32 *dst asm ("ep"); -#endif - - AS85EP1_CSC(0) = 0x0403; - AS85EP1_BCT(0) = 0xB8B8; - AS85EP1_DWC(0) = 0x0104; - AS85EP1_BCC = 0x0012; - AS85EP1_ASC = 0; - AS85EP1_LBS = 0x00A9; - - AS85EP1_PORT_PMC(6) = 0xFF; /* valid A0,A1,A20-A25 */ - AS85EP1_PORT_PMC(7) = 0x0E; /* valid CS1-CS3 */ - AS85EP1_PORT_PMC(9) = 0xFF; /* valid D16-D23 */ - AS85EP1_PORT_PMC(10) = 0xFF; /* valid D24-D31 */ - - AS85EP1_RFS(1) = 0x800c; - AS85EP1_RFS(3) = 0x800c; - AS85EP1_SCR(1) = 0x20A9; - AS85EP1_SCR(3) = 0x20A9; - -#ifndef CONFIG_ROM_KERNEL - /* The early chip we have is buggy, and writing the interrupt - vectors into low RAM may screw up, so for non-ROM kernels, we - only rely on the reset vector being downloaded, and copy the - rest of the interrupt vectors into place here. The specific bug - is that writing address N, where (N & 0x10) == 0x10, will _also_ - write to address (N - 0x10). We avoid this (effectively) by - writing in 16-byte chunks backwards from the end. */ - - AS85EP1_IRAMM = 0x3; /* "write-mode" for the internal instruction memory */ - - src = (u32 *)(((u32)&_intv_copy_src_end - 1) & ~0xF); - dst = (u32 *)&_intv_copy_dst_start - + (src - (u32 *)&_intv_copy_src_start); - do { - u32 t0 = src[0], t1 = src[1], t2 = src[2], t3 = src[3]; - dst[0] = t0; dst[1] = t1; dst[2] = t2; dst[3] = t3; - dst -= 4; - src -= 4; - } while (src > (u32 *)&_intv_copy_src_start); - - AS85EP1_IRAMM = 0x0; /* "read-mode" for the internal instruction memory */ -#endif /* !CONFIG_ROM_KERNEL */ - - v850e_intc_disable_irqs (); -} - -void __init mach_setup (char **cmdline) -{ - AS85EP1_PORT_PMC (LEDS_PORT) = 0; /* Make the LEDs port an I/O port. */ - AS85EP1_PORT_PM (LEDS_PORT) = 0; /* Make all the bits output pins. */ - mach_tick = as85ep1_led_tick; -} - -void __init mach_get_physical_ram (unsigned long *ram_start, - unsigned long *ram_len) -{ - *ram_start = RAM_START; - *ram_len = RAM_END - RAM_START; -} - -/* Convenience macros. */ -#define SRAM_END (SRAM_ADDR + SRAM_SIZE) -#define SDRAM_END (SDRAM_ADDR + SDRAM_SIZE) - -void __init mach_reserve_bootmem () -{ - if (SDRAM_ADDR < RAM_END && SDRAM_ADDR > RAM_START) - /* We can't use the space between SRAM and SDRAM, so - prevent the kernel from trying. */ - reserve_bootmem(SRAM_END, SDRAM_ADDR - SRAM_END, - BOOTMEM_DEFAULT); -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Start hardware timer. */ - v850e_timer_d_configure (0, HZ); - /* Install timer interrupt handler. */ - setup_irq (IRQ_INTCMD(0), timer_action); -} - -static struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_MACH_IRQS, 1, 7 }, - { "CCC", IRQ_INTCCC(0), IRQ_INTCCC_NUM, 1, 5 }, - { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 }, - { "SRE", IRQ_INTSRE(0), IRQ_INTSRE_NUM, 3, 3 }, - { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 3, 4 }, - { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 3, 5 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -void __init mach_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - -void machine_restart (char *__unused) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - asm ("jmp r0"); /* Jump to the reset vector. */ -} - -void machine_halt (void) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - local_irq_disable (); /* Ignore all interrupts. */ - AS85EP1_PORT_IO (LEDS_PORT) = 0xAA; /* Note that we halted. */ - for (;;) - asm ("halt; nop; nop; nop; nop; nop"); -} - -void machine_power_off (void) -{ - machine_halt (); -} - -/* Called before configuring an on-chip UART. */ -void as85ep1_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud) -{ - /* Make the shared uart/port pins be uart pins. */ - AS85EP1_PORT_PMC(3) |= (0x5 << chan); - - /* The AS85EP1 connects some general-purpose I/O pins on the CPU to - the RTS/CTS lines of UART 1's serial connection. I/O pins P53 - and P54 are RTS and CTS respectively. */ - if (chan == 1) { - /* Put P53 & P54 in I/O port mode. */ - AS85EP1_PORT_PMC(5) &= ~0x18; - /* Make P53 an output, and P54 an input. */ - AS85EP1_PORT_PM(5) |= 0x10; - } -} - -/* Minimum and maximum bounds for the moving upper LED boundary in the - clock tick display. */ -#define MIN_MAX_POS 0 -#define MAX_MAX_POS 7 - -/* There are MAX_MAX_POS^2 - MIN_MAX_POS^2 cycles in the animation, so if - we pick 6 and 0 as above, we get 49 cycles, which is when divided into - the standard 100 value for HZ, gives us an almost 1s total time. */ -#define TICKS_PER_FRAME \ - (HZ / (MAX_MAX_POS * MAX_MAX_POS - MIN_MAX_POS * MIN_MAX_POS)) - -static void as85ep1_led_tick () -{ - static unsigned counter = 0; - - if (++counter == TICKS_PER_FRAME) { - static int pos = 0, max_pos = MAX_MAX_POS, dir = 1; - - if (dir > 0 && pos == max_pos) { - dir = -1; - if (max_pos == MIN_MAX_POS) - max_pos = MAX_MAX_POS; - else - max_pos--; - } else { - if (dir < 0 && pos == 0) - dir = 1; - - if (pos + dir <= max_pos) { - /* Each bit of port 0 has a LED. */ - set_bit (pos, &AS85EP1_PORT_IO(LEDS_PORT)); - pos += dir; - clear_bit (pos, &AS85EP1_PORT_IO(LEDS_PORT)); - } - } - - counter = 0; - } -} diff --git a/arch/v850/kernel/as85ep1.ld b/arch/v850/kernel/as85ep1.ld deleted file mode 100644 index ef2c4399063e..000000000000 --- a/arch/v850/kernel/as85ep1.ld +++ /dev/null @@ -1,49 +0,0 @@ -/* Linker script for the NEC AS85EP1 V850E evaluation board - (CONFIG_V850E_AS85EP1). */ - -MEMORY { - /* 1MB of internal instruction memory. */ - iMEM0 : ORIGIN = 0, LENGTH = 0x00100000 - - /* 1MB of static RAM. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - - /* About 58MB of DRAM. This can actually be at one of two - positions, determined by jump JP3; we have to use the first - position because the second is partially out of processor - instruction addressing range (though in the second position - there's actually 64MB available). */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - .resetv : { - __intv_start = . ; - *(.intv.reset) /* Reset vector */ - } > iMEM0 - - .sram : { - RAMK_KRAM_CONTENTS - - /* We stick most of the interrupt vectors here; they'll be - copied into the proper location by the early init code (we - can't put them directly in the right place because of - hardware bugs). The vectors shouldn't need to be - relocated, so we don't have to use `> ... AT> ...' to - split the load/vm addresses (and we can't because of - problems with the loader). */ - . = ALIGN (0x10) ; - __intv_copy_src_start = . ; - *(.intv.common) /* Vectors common to all v850e proc. */ - *(.intv.mach) /* Machine-specific int. vectors. */ - . = ALIGN (0x10) ; - __intv_copy_src_end = . ; - } > SRAM - - /* Where we end up putting the vectors. */ - __intv_copy_dst_start = 0x10 ; - __intv_copy_dst_end = __intv_copy_dst_start + (__intv_copy_src_end - __intv_copy_src_start) ; - __intv_end = __intv_copy_dst_end ; - - .root : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/asm-offsets.c b/arch/v850/kernel/asm-offsets.c deleted file mode 100644 index 581e6986a776..000000000000 --- a/arch/v850/kernel/asm-offsets.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This program is used to generate definitions needed by - * assembly language modules. - * - * We use the technique used in the OSF Mach kernel code: - * generate asm statements containing #defines, - * compile this file to assembler, and then extract the - * #defines from the assembly-language output. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -int main (void) -{ - /* offsets into the task struct */ - DEFINE (TASK_STATE, offsetof (struct task_struct, state)); - DEFINE (TASK_FLAGS, offsetof (struct task_struct, flags)); - DEFINE (TASK_PTRACE, offsetof (struct task_struct, ptrace)); - DEFINE (TASK_BLOCKED, offsetof (struct task_struct, blocked)); - DEFINE (TASK_THREAD, offsetof (struct task_struct, thread)); - DEFINE (TASK_THREAD_INFO, offsetof (struct task_struct, stack)); - DEFINE (TASK_MM, offsetof (struct task_struct, mm)); - DEFINE (TASK_ACTIVE_MM, offsetof (struct task_struct, active_mm)); - DEFINE (TASK_PID, offsetof (struct task_struct, pid)); - - /* offsets into the kernel_stat struct */ - DEFINE (STAT_IRQ, offsetof (struct kernel_stat, irqs)); - - - /* signal defines */ - DEFINE (SIGSEGV, SIGSEGV); - DEFINE (SEGV_MAPERR, SEGV_MAPERR); - DEFINE (SIGTRAP, SIGTRAP); - DEFINE (SIGCHLD, SIGCHLD); - DEFINE (SIGILL, SIGILL); - DEFINE (TRAP_TRACE, TRAP_TRACE); - - /* ptrace flag bits */ - DEFINE (PT_PTRACED, PT_PTRACED); - DEFINE (PT_DTRACE, PT_DTRACE); - - /* error values */ - DEFINE (ENOSYS, ENOSYS); - - /* clone flag bits */ - DEFINE (CLONE_VFORK, CLONE_VFORK); - DEFINE (CLONE_VM, CLONE_VM); - - return 0; -} diff --git a/arch/v850/kernel/bug.c b/arch/v850/kernel/bug.c deleted file mode 100644 index c78cf750915a..000000000000 --- a/arch/v850/kernel/bug.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * arch/v850/kernel/bug.c -- Bug reporting functions - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -/* We should use __builtin_return_address, but it doesn't work in gcc-2.90 - (which is currently our standard compiler on the v850). */ -#define ret_addr() ({ register u32 lp asm ("lp"); lp; }) -#define stack_addr() ({ register u32 sp asm ("sp"); sp; }) - -void __bug () -{ - printk (KERN_CRIT "kernel BUG at PC 0x%x (SP ~0x%x)!\n", - ret_addr() - 4, /* - 4 for `jarl' */ - stack_addr()); - machine_halt (); -} - -int bad_trap (int trap_num, struct pt_regs *regs) -{ - printk (KERN_CRIT - "unimplemented trap %d called at 0x%08lx, pid %d!\n", - trap_num, regs->pc, current->pid); - return -ENOSYS; -} - -#ifdef CONFIG_RESET_GUARD -void unexpected_reset (unsigned long ret_addr, unsigned long kmode, - struct task_struct *task, unsigned long sp) -{ - printk (KERN_CRIT - "unexpected reset in %s mode, pid %d" - " (ret_addr = 0x%lx, sp = 0x%lx)\n", - kmode ? "kernel" : "user", - task ? task->pid : -1, - ret_addr, sp); - - machine_halt (); -} -#endif /* CONFIG_RESET_GUARD */ - - - -struct spec_reg_name { - const char *name; - int gpr; -}; - -struct spec_reg_name spec_reg_names[] = { - { "sp", GPR_SP }, - { "gp", GPR_GP }, - { "tp", GPR_TP }, - { "ep", GPR_EP }, - { "lp", GPR_LP }, - { 0, 0 } -}; - -void show_regs (struct pt_regs *regs) -{ - int gpr_base, gpr_offs; - - printk (" pc 0x%08lx psw 0x%08lx kernel_mode %d\n", - regs->pc, regs->psw, regs->kernel_mode); - printk (" ctpc 0x%08lx ctpsw 0x%08lx ctbp 0x%08lx\n", - regs->ctpc, regs->ctpsw, regs->ctbp); - - for (gpr_base = 0; gpr_base < NUM_GPRS; gpr_base += 4) { - for (gpr_offs = 0; gpr_offs < 4; gpr_offs++) { - int gpr = gpr_base + gpr_offs; - long val = regs->gpr[gpr]; - struct spec_reg_name *srn; - - for (srn = spec_reg_names; srn->name; srn++) - if (srn->gpr == gpr) - break; - - if (srn->name) - printk ("%7s 0x%08lx", srn->name, val); - else - printk (" r%02d 0x%08lx", gpr, val); - } - - printk ("\n"); - } -} - -/* - * TASK is a pointer to the task whose backtrace we want to see (or NULL - * for current task), SP is the stack pointer of the first frame that - * should be shown in the back trace (or NULL if the entire call-chain of - * the task should be shown). - */ -void show_stack (struct task_struct *task, unsigned long *sp) -{ - unsigned long addr, end; - - if (sp) - addr = (unsigned long)sp; - else if (task) - addr = task_sp (task); - else - addr = stack_addr (); - - addr = addr & ~3; - end = (addr + THREAD_SIZE - 1) & THREAD_MASK; - - while (addr < end) { - printk ("%8lX: ", addr); - while (addr < end) { - printk (" %8lX", *(unsigned long *)addr); - addr += sizeof (unsigned long); - if (! (addr & 0xF)) - break; - } - printk ("\n"); - } -} - -void dump_stack () -{ - show_stack (0, 0); -} - -EXPORT_SYMBOL(dump_stack); diff --git a/arch/v850/kernel/entry.S b/arch/v850/kernel/entry.S deleted file mode 100644 index e4327a8d6bcd..000000000000 --- a/arch/v850/kernel/entry.S +++ /dev/null @@ -1,1121 +0,0 @@ -/* - * arch/v850/kernel/entry.S -- Low-level system-call handling, trap handlers, - * and context-switching - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - - -/* Make a slightly more convenient alias for C_SYMBOL_NAME. */ -#define CSYM C_SYMBOL_NAME - - -/* The offset of the struct pt_regs in a state-save-frame on the stack. */ -#define PTO STATE_SAVE_PT_OFFSET - - -/* Save argument registers to the state-save-frame pointed to by EP. */ -#define SAVE_ARG_REGS \ - sst.w r6, PTO+PT_GPR(6)[ep]; \ - sst.w r7, PTO+PT_GPR(7)[ep]; \ - sst.w r8, PTO+PT_GPR(8)[ep]; \ - sst.w r9, PTO+PT_GPR(9)[ep] -/* Restore argument registers from the state-save-frame pointed to by EP. */ -#define RESTORE_ARG_REGS \ - sld.w PTO+PT_GPR(6)[ep], r6; \ - sld.w PTO+PT_GPR(7)[ep], r7; \ - sld.w PTO+PT_GPR(8)[ep], r8; \ - sld.w PTO+PT_GPR(9)[ep], r9 - -/* Save value return registers to the state-save-frame pointed to by EP. */ -#define SAVE_RVAL_REGS \ - sst.w r10, PTO+PT_GPR(10)[ep]; \ - sst.w r11, PTO+PT_GPR(11)[ep] -/* Restore value return registers from the state-save-frame pointed to by EP. */ -#define RESTORE_RVAL_REGS \ - sld.w PTO+PT_GPR(10)[ep], r10; \ - sld.w PTO+PT_GPR(11)[ep], r11 - - -#define SAVE_CALL_CLOBBERED_REGS_BEFORE_ARGS \ - sst.w r1, PTO+PT_GPR(1)[ep]; \ - sst.w r5, PTO+PT_GPR(5)[ep] -#define SAVE_CALL_CLOBBERED_REGS_AFTER_RVAL \ - sst.w r12, PTO+PT_GPR(12)[ep]; \ - sst.w r13, PTO+PT_GPR(13)[ep]; \ - sst.w r14, PTO+PT_GPR(14)[ep]; \ - sst.w r15, PTO+PT_GPR(15)[ep]; \ - sst.w r16, PTO+PT_GPR(16)[ep]; \ - sst.w r17, PTO+PT_GPR(17)[ep]; \ - sst.w r18, PTO+PT_GPR(18)[ep]; \ - sst.w r19, PTO+PT_GPR(19)[ep] -#define RESTORE_CALL_CLOBBERED_REGS_BEFORE_ARGS \ - sld.w PTO+PT_GPR(1)[ep], r1; \ - sld.w PTO+PT_GPR(5)[ep], r5 -#define RESTORE_CALL_CLOBBERED_REGS_AFTER_RVAL \ - sld.w PTO+PT_GPR(12)[ep], r12; \ - sld.w PTO+PT_GPR(13)[ep], r13; \ - sld.w PTO+PT_GPR(14)[ep], r14; \ - sld.w PTO+PT_GPR(15)[ep], r15; \ - sld.w PTO+PT_GPR(16)[ep], r16; \ - sld.w PTO+PT_GPR(17)[ep], r17; \ - sld.w PTO+PT_GPR(18)[ep], r18; \ - sld.w PTO+PT_GPR(19)[ep], r19 - -/* Save `call clobbered' registers to the state-save-frame pointed to by EP. */ -#define SAVE_CALL_CLOBBERED_REGS \ - SAVE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \ - SAVE_ARG_REGS; \ - SAVE_RVAL_REGS; \ - SAVE_CALL_CLOBBERED_REGS_AFTER_RVAL -/* Restore `call clobbered' registers from the state-save-frame pointed to - by EP. */ -#define RESTORE_CALL_CLOBBERED_REGS \ - RESTORE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \ - RESTORE_ARG_REGS; \ - RESTORE_RVAL_REGS; \ - RESTORE_CALL_CLOBBERED_REGS_AFTER_RVAL - -/* Save `call clobbered' registers except for the return-value registers - to the state-save-frame pointed to by EP. */ -#define SAVE_CALL_CLOBBERED_REGS_NO_RVAL \ - SAVE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \ - SAVE_ARG_REGS; \ - SAVE_CALL_CLOBBERED_REGS_AFTER_RVAL -/* Restore `call clobbered' registers except for the return-value registers - from the state-save-frame pointed to by EP. */ -#define RESTORE_CALL_CLOBBERED_REGS_NO_RVAL \ - RESTORE_CALL_CLOBBERED_REGS_BEFORE_ARGS; \ - RESTORE_ARG_REGS; \ - RESTORE_CALL_CLOBBERED_REGS_AFTER_RVAL - -/* Save `call saved' registers to the state-save-frame pointed to by EP. */ -#define SAVE_CALL_SAVED_REGS \ - sst.w r2, PTO+PT_GPR(2)[ep]; \ - sst.w r20, PTO+PT_GPR(20)[ep]; \ - sst.w r21, PTO+PT_GPR(21)[ep]; \ - sst.w r22, PTO+PT_GPR(22)[ep]; \ - sst.w r23, PTO+PT_GPR(23)[ep]; \ - sst.w r24, PTO+PT_GPR(24)[ep]; \ - sst.w r25, PTO+PT_GPR(25)[ep]; \ - sst.w r26, PTO+PT_GPR(26)[ep]; \ - sst.w r27, PTO+PT_GPR(27)[ep]; \ - sst.w r28, PTO+PT_GPR(28)[ep]; \ - sst.w r29, PTO+PT_GPR(29)[ep] -/* Restore `call saved' registers from the state-save-frame pointed to by EP. */ -#define RESTORE_CALL_SAVED_REGS \ - sld.w PTO+PT_GPR(2)[ep], r2; \ - sld.w PTO+PT_GPR(20)[ep], r20; \ - sld.w PTO+PT_GPR(21)[ep], r21; \ - sld.w PTO+PT_GPR(22)[ep], r22; \ - sld.w PTO+PT_GPR(23)[ep], r23; \ - sld.w PTO+PT_GPR(24)[ep], r24; \ - sld.w PTO+PT_GPR(25)[ep], r25; \ - sld.w PTO+PT_GPR(26)[ep], r26; \ - sld.w PTO+PT_GPR(27)[ep], r27; \ - sld.w PTO+PT_GPR(28)[ep], r28; \ - sld.w PTO+PT_GPR(29)[ep], r29 - - -/* Save the PC stored in the special register SAVEREG to the state-save-frame - pointed to by EP. r19 is clobbered. */ -#define SAVE_PC(savereg) \ - stsr SR_ ## savereg, r19; \ - sst.w r19, PTO+PT_PC[ep] -/* Restore the PC from the state-save-frame pointed to by EP, to the special - register SAVEREG. LP is clobbered (it is used as a scratch register - because the POP_STATE macro restores it, and this macro is usually used - inside POP_STATE). */ -#define RESTORE_PC(savereg) \ - sld.w PTO+PT_PC[ep], lp; \ - ldsr lp, SR_ ## savereg -/* Save the PSW register stored in the special register SAVREG to the - state-save-frame pointed to by EP. r19 is clobbered. */ -#define SAVE_PSW(savereg) \ - stsr SR_ ## savereg, r19; \ - sst.w r19, PTO+PT_PSW[ep] -/* Restore the PSW register from the state-save-frame pointed to by EP, to - the special register SAVEREG. LP is clobbered (it is used as a scratch - register because the POP_STATE macro restores it, and this macro is - usually used inside POP_STATE). */ -#define RESTORE_PSW(savereg) \ - sld.w PTO+PT_PSW[ep], lp; \ - ldsr lp, SR_ ## savereg - -/* Save CTPC/CTPSW/CTBP registers to the state-save-frame pointed to by REG. - r19 is clobbered. */ -#define SAVE_CT_REGS \ - stsr SR_CTPC, r19; \ - sst.w r19, PTO+PT_CTPC[ep]; \ - stsr SR_CTPSW, r19; \ - sst.w r19, PTO+PT_CTPSW[ep]; \ - stsr SR_CTBP, r19; \ - sst.w r19, PTO+PT_CTBP[ep] -/* Restore CTPC/CTPSW/CTBP registers from the state-save-frame pointed to by EP. - LP is clobbered (it is used as a scratch register because the POP_STATE - macro restores it, and this macro is usually used inside POP_STATE). */ -#define RESTORE_CT_REGS \ - sld.w PTO+PT_CTPC[ep], lp; \ - ldsr lp, SR_CTPC; \ - sld.w PTO+PT_CTPSW[ep], lp; \ - ldsr lp, SR_CTPSW; \ - sld.w PTO+PT_CTBP[ep], lp; \ - ldsr lp, SR_CTBP - - -/* Push register state, except for the stack pointer, on the stack in the - form of a state-save-frame (plus some extra padding), in preparation for - a system call. This macro makes sure that the EP, GP, and LP - registers are saved, and TYPE identifies the set of extra registers to - be saved as well. Also copies (the new value of) SP to EP. */ -#define PUSH_STATE(type) \ - addi -STATE_SAVE_SIZE, sp, sp; /* Make room on the stack. */ \ - st.w ep, PTO+PT_GPR(GPR_EP)[sp]; \ - mov sp, ep; \ - sst.w gp, PTO+PT_GPR(GPR_GP)[ep]; \ - sst.w lp, PTO+PT_GPR(GPR_LP)[ep]; \ - type ## _STATE_SAVER -/* Pop a register state pushed by PUSH_STATE, except for the stack pointer, - from the stack. */ -#define POP_STATE(type) \ - mov sp, ep; \ - type ## _STATE_RESTORER; \ - sld.w PTO+PT_GPR(GPR_GP)[ep], gp; \ - sld.w PTO+PT_GPR(GPR_LP)[ep], lp; \ - sld.w PTO+PT_GPR(GPR_EP)[ep], ep; \ - addi STATE_SAVE_SIZE, sp, sp /* Clean up our stack space. */ - - -/* Switch to the kernel stack if necessary, and push register state on the - stack in the form of a state-save-frame. Also load the current task - pointer if switching from user mode. The stack-pointer (r3) should have - already been saved to the memory location SP_SAVE_LOC (the reason for - this is that the interrupt vectors may be beyond a 22-bit signed offset - jump from the actual interrupt handler, and this allows them to save the - stack-pointer and use that register to do an indirect jump). This macro - makes sure that `special' registers, system registers, and the stack - pointer are saved; TYPE identifies the set of extra registers to be - saved as well. SYSCALL_NUM is the register in which the system-call - number this state is for is stored (r0 if this isn't a system call). - Interrupts should already be disabled when calling this. */ -#define SAVE_STATE(type, syscall_num, sp_save_loc) \ - tst1 0, KM; /* See if already in kernel mode. */ \ - bz 1f; \ - ld.w sp_save_loc, sp; /* ... yes, use saved SP. */ \ - br 2f; \ -1: ld.w KSP, sp; /* ... no, switch to kernel stack. */ \ -2: PUSH_STATE(type); \ - ld.b KM, r19; /* Remember old kernel-mode. */ \ - sst.w r19, PTO+PT_KERNEL_MODE[ep]; \ - ld.w sp_save_loc, r19; /* Remember old SP. */ \ - sst.w r19, PTO+PT_GPR(GPR_SP)[ep]; \ - mov 1, r19; /* Now definitely in kernel-mode. */ \ - st.b r19, KM; \ - GET_CURRENT_TASK(CURRENT_TASK); /* Fetch the current task pointer. */ \ - /* Save away the syscall number. */ \ - sst.w syscall_num, PTO+PT_CUR_SYSCALL[ep] - - -/* Save register state not normally saved by PUSH_STATE for TYPE, to the - state-save-frame on the stack; also copies SP to EP. r19 may be trashed. */ -#define SAVE_EXTRA_STATE(type) \ - mov sp, ep; \ - type ## _EXTRA_STATE_SAVER -/* Restore register state not normally restored by POP_STATE for TYPE, - from the state-save-frame on the stack; also copies SP to EP. - r19 may be trashed. */ -#define RESTORE_EXTRA_STATE(type) \ - mov sp, ep; \ - type ## _EXTRA_STATE_RESTORER - -/* Save any call-clobbered registers not normally saved by PUSH_STATE for - TYPE, to the state-save-frame on the stack. - EP may be trashed, but is not guaranteed to contain a copy of SP - (unlike after most SAVE_... macros). r19 may be trashed. */ -#define SAVE_EXTRA_STATE_FOR_SCHEDULE(type) \ - type ## _SCHEDULE_EXTRA_STATE_SAVER -/* Restore any call-clobbered registers not normally restored by - POP_STATE for TYPE, to the state-save-frame on the stack. - EP may be trashed, but is not guaranteed to contain a copy of SP - (unlike after most RESTORE_... macros). r19 may be trashed. */ -#define RESTORE_EXTRA_STATE_FOR_SCHEDULE(type) \ - type ## _SCHEDULE_EXTRA_STATE_RESTORER - - -/* These are extra_state_saver/restorer values for a user trap. Note - that we save the argument registers so that restarted syscalls will - function properly (otherwise it wouldn't be necessary), and we must - _not_ restore the return-value registers (so that traps can return a - value!), but call-clobbered registers are not saved at all, as the - caller of the syscall function should have saved them. */ - -#define TRAP_RET reti -/* Traps don't save call-clobbered registers (but do still save arg regs). - We preserve PSw to keep long-term state, namely interrupt status (for traps - from kernel-mode), and the single-step flag (for user traps). */ -#define TRAP_STATE_SAVER \ - SAVE_ARG_REGS; \ - SAVE_PC(EIPC); \ - SAVE_PSW(EIPSW) -/* When traps return, they just leave call-clobbered registers (except for arg - regs) with whatever value they have from the kernel. Traps don't preserve - the PSW, but we zero EIPSW to ensure it doesn't contain anything dangerous - (in particular, the single-step flag). */ -#define TRAP_STATE_RESTORER \ - RESTORE_ARG_REGS; \ - RESTORE_PC(EIPC); \ - RESTORE_PSW(EIPSW) -/* Save registers not normally saved by traps. We need to save r12, even - though it's nominally call-clobbered, because it's used when restarting - a system call (the signal-handling path uses SAVE_EXTRA_STATE, and - expects r12 to be restored when the trap returns). */ -#define TRAP_EXTRA_STATE_SAVER \ - SAVE_RVAL_REGS; \ - sst.w r12, PTO+PT_GPR(12)[ep]; \ - SAVE_CALL_SAVED_REGS; \ - SAVE_CT_REGS -#define TRAP_EXTRA_STATE_RESTORER \ - RESTORE_RVAL_REGS; \ - sld.w PTO+PT_GPR(12)[ep], r12; \ - RESTORE_CALL_SAVED_REGS; \ - RESTORE_CT_REGS -/* Save registers prior to calling scheduler (just before trap returns). - We have to save the return-value registers to preserve the trap's return - value. Note that ..._SCHEDULE_EXTRA_STATE_SAVER, unlike most ..._SAVER - macros, is required to setup EP itself if EP is needed (this is because - in many cases, the macro is empty). */ -#define TRAP_SCHEDULE_EXTRA_STATE_SAVER \ - mov sp, ep; \ - SAVE_RVAL_REGS -/* Note that ..._SCHEDULE_EXTRA_STATE_RESTORER, unlike most ..._RESTORER - macros, is required to setup EP itself if EP is needed (this is because - in many cases, the macro is empty). */ -#define TRAP_SCHEDULE_EXTRA_STATE_RESTORER \ - mov sp, ep; \ - RESTORE_RVAL_REGS - -/* Register saving/restoring for maskable interrupts. */ -#define IRQ_RET reti -#define IRQ_STATE_SAVER \ - SAVE_CALL_CLOBBERED_REGS; \ - SAVE_PC(EIPC); \ - SAVE_PSW(EIPSW) -#define IRQ_STATE_RESTORER \ - RESTORE_CALL_CLOBBERED_REGS; \ - RESTORE_PC(EIPC); \ - RESTORE_PSW(EIPSW) -#define IRQ_EXTRA_STATE_SAVER \ - SAVE_CALL_SAVED_REGS; \ - SAVE_CT_REGS -#define IRQ_EXTRA_STATE_RESTORER \ - RESTORE_CALL_SAVED_REGS; \ - RESTORE_CT_REGS -#define IRQ_SCHEDULE_EXTRA_STATE_SAVER /* nothing */ -#define IRQ_SCHEDULE_EXTRA_STATE_RESTORER /* nothing */ - -/* Register saving/restoring for non-maskable interrupts. */ -#define NMI_RET reti -#define NMI_STATE_SAVER \ - SAVE_CALL_CLOBBERED_REGS; \ - SAVE_PC(FEPC); \ - SAVE_PSW(FEPSW); -#define NMI_STATE_RESTORER \ - RESTORE_CALL_CLOBBERED_REGS; \ - RESTORE_PC(FEPC); \ - RESTORE_PSW(FEPSW); -#define NMI_EXTRA_STATE_SAVER \ - SAVE_CALL_SAVED_REGS; \ - SAVE_CT_REGS -#define NMI_EXTRA_STATE_RESTORER \ - RESTORE_CALL_SAVED_REGS; \ - RESTORE_CT_REGS -#define NMI_SCHEDULE_EXTRA_STATE_SAVER /* nothing */ -#define NMI_SCHEDULE_EXTRA_STATE_RESTORER /* nothing */ - -/* Register saving/restoring for debug traps. */ -#define DBTRAP_RET .long 0x014607E0 /* `dbret', but gas doesn't support it. */ -#define DBTRAP_STATE_SAVER \ - SAVE_CALL_CLOBBERED_REGS; \ - SAVE_PC(DBPC); \ - SAVE_PSW(DBPSW) -#define DBTRAP_STATE_RESTORER \ - RESTORE_CALL_CLOBBERED_REGS; \ - RESTORE_PC(DBPC); \ - RESTORE_PSW(DBPSW) -#define DBTRAP_EXTRA_STATE_SAVER \ - SAVE_CALL_SAVED_REGS; \ - SAVE_CT_REGS -#define DBTRAP_EXTRA_STATE_RESTORER \ - RESTORE_CALL_SAVED_REGS; \ - RESTORE_CT_REGS -#define DBTRAP_SCHEDULE_EXTRA_STATE_SAVER /* nothing */ -#define DBTRAP_SCHEDULE_EXTRA_STATE_RESTORER /* nothing */ - -/* Register saving/restoring for a context switch. We don't need to save - too many registers, because context-switching looks like a function call - (via the function `switch_thread'), so callers will save any - call-clobbered registers themselves. We do need to save the CT regs, as - they're normally not saved during kernel entry (the kernel doesn't use - them). We save PSW so that interrupt-status state will correctly follow - each thread (mostly NMI vs. normal-IRQ/trap), though for the most part - it doesn't matter since threads are always in almost exactly the same - processor state during a context switch. The stack pointer and return - value are handled by switch_thread itself. */ -#define SWITCH_STATE_SAVER \ - SAVE_CALL_SAVED_REGS; \ - SAVE_PSW(PSW); \ - SAVE_CT_REGS -#define SWITCH_STATE_RESTORER \ - RESTORE_CALL_SAVED_REGS; \ - RESTORE_PSW(PSW); \ - RESTORE_CT_REGS - - -/* Restore register state from the state-save-frame on the stack, switch back - to the user stack if necessary, and return from the trap/interrupt. - EXTRA_STATE_RESTORER is a sequence of assembly language statements to - restore anything not restored by this macro. Only registers not saved by - the C compiler are restored (that is, R3(sp), R4(gp), R31(lp), and - anything restored by EXTRA_STATE_RESTORER). */ -#define RETURN(type) \ - ld.b PTO+PT_KERNEL_MODE[sp], r19; \ - di; /* Disable interrupts */ \ - cmp r19, r0; /* See if returning to kernel mode, */\ - bne 2f; /* ... if so, skip resched &c. */ \ - \ - /* We're returning to user mode, so check for various conditions that \ - trigger rescheduling. */ \ - GET_CURRENT_THREAD(r18); \ - ld.w TI_FLAGS[r18], r19; \ - andi _TIF_NEED_RESCHED, r19, r0; \ - bnz 3f; /* Call the scheduler. */ \ -5: andi _TIF_SIGPENDING, r19, r18; \ - ld.w TASK_PTRACE[CURRENT_TASK], r19; /* ptrace flags */ \ - or r18, r19; /* see if either is non-zero */ \ - bnz 4f; /* if so, handle them */ \ - \ -/* Return to user state. */ \ -1: st.b r0, KM; /* Now officially in user state. */ \ - \ -/* Final return. The stack-pointer fiddling is not needed when returning \ - to kernel-mode, but they don't hurt, and this way we can share the \ - (sometimes rather lengthy) POP_STATE macro. */ \ -2: POP_STATE(type); \ - st.w sp, KSP; /* Save the kernel stack pointer. */ \ - ld.w PT_GPR(GPR_SP)-PT_SIZE[sp], sp; /* Restore stack pointer. */ \ - type ## _RET; /* Return from the trap/interrupt. */ \ - \ -/* Call the scheduler before returning from a syscall/trap. */ \ -3: SAVE_EXTRA_STATE_FOR_SCHEDULE(type); /* Prepare to call scheduler. */ \ - jarl call_scheduler, lp; /* Call scheduler */ \ - di; /* The scheduler enables interrupts */\ - RESTORE_EXTRA_STATE_FOR_SCHEDULE(type); \ - GET_CURRENT_THREAD(r18); \ - ld.w TI_FLAGS[r18], r19; \ - br 5b; /* Continue with return path. */ \ - \ -/* Handle a signal or ptraced process return. \ - r18 should be non-zero if there are pending signals. */ \ -4: /* Not all registers are saved by the normal trap/interrupt entry \ - points (for instance, call-saved registers (because the normal \ - C-compiler calling sequence in the kernel makes sure they're \ - preserved), and call-clobbered registers in the case of \ - traps), but signal handlers may want to examine or change the \ - complete register state. Here we save anything not saved by \ - the normal entry sequence, so that it may be safely restored \ - (in a possibly modified form) after do_signal returns. */ \ - SAVE_EXTRA_STATE(type); /* Save state not saved by entry. */ \ - jarl handle_signal_or_ptrace_return, lp; \ - RESTORE_EXTRA_STATE(type); /* Restore extra regs. */ \ - br 1b - - -/* Jump to the appropriate function for the system call number in r12 - (r12 is not preserved), or return an error if r12 is not valid. The - LP register should point to the location where the called function - should return. [note that MAKE_SYS_CALL uses label 1] */ -#define MAKE_SYS_CALL \ - /* Figure out which function to use for this system call. */ \ - shl 2, r12; \ - /* See if the system call number is valid. */ \ - addi lo(CSYM(sys_call_table) - sys_call_table_end), r12, r0; \ - bnh 1f; \ - mov hilo(CSYM(sys_call_table)), r19; \ - add r19, r12; \ - ld.w 0[r12], r12; \ - /* Make the system call. */ \ - jmp [r12]; \ - /* The syscall number is invalid, return an error. */ \ -1: addi -ENOSYS, r0, r10; \ - jmp [lp] - - - .text - -/* - * User trap. - * - * Trap 0 system calls are also handled here. - * - * The stack-pointer (r3) should have already been saved to the memory - * location ENTRY_SP (the reason for this is that the interrupt vectors may be - * beyond a 22-bit signed offset jump from the actual interrupt handler, and - * this allows them to save the stack-pointer and use that register to do an - * indirect jump). - * - * Syscall protocol: - * Syscall number in r12, args in r6-r9 - * Return value in r10 - */ -G_ENTRY(trap): - SAVE_STATE (TRAP, r12, ENTRY_SP) // Save registers. - stsr SR_ECR, r19 // Find out which trap it was. - ei // Enable interrupts. - mov hilo(ret_from_trap), lp // where the trap should return - - // The following two shifts (1) clear out extraneous NMI data in the - // upper 16-bits, (2) convert the 0x40 - 0x5f range of trap ECR - // numbers into the (0-31) << 2 range we want, (3) set the flags. - shl 27, r19 // chop off all high bits - shr 25, r19 // scale back down and then << 2 - bnz 2f // See if not trap 0. - - // Trap 0 is a `short' system call, skip general trap table. - MAKE_SYS_CALL // Jump to the syscall function. - -2: // For other traps, use a table lookup. - mov hilo(CSYM(trap_table)), r18 - add r19, r18 - ld.w 0[r18], r18 - jmp [r18] // Jump to the trap handler. -END(trap) - -/* This is just like ret_from_trap, but first restores extra registers - saved by some wrappers. */ -L_ENTRY(restore_extra_regs_and_ret_from_trap): - RESTORE_EXTRA_STATE(TRAP) - // fall through -END(restore_extra_regs_and_ret_from_trap) - -/* Entry point used to return from a syscall/trap. */ -L_ENTRY(ret_from_trap): - RETURN(TRAP) -END(ret_from_trap) - - -/* This the initial entry point for a new child thread, with an appropriate - stack in place that makes it look that the child is in the middle of an - syscall. This function is actually `returned to' from switch_thread - (copy_thread makes ret_from_fork the return address in each new thread's - saved context). */ -C_ENTRY(ret_from_fork): - mov r10, r6 // switch_thread returns the prev task. - jarl CSYM(schedule_tail), lp // ...which is schedule_tail's arg - mov r0, r10 // Child's fork call should return 0. - br ret_from_trap // Do normal trap return. -C_END(ret_from_fork) - - -/* - * Trap 1: `long' system calls - * `Long' syscall protocol: - * Syscall number in r12, args in r6-r9, r13-r14 - * Return value in r10 - */ -L_ENTRY(syscall_long): - // Push extra arguments on the stack. Note that by default, the trap - // handler reserves enough stack space for 6 arguments, so we don't - // have to make any additional room. - st.w r13, 16[sp] // arg 5 - st.w r14, 20[sp] // arg 6 - - // Make sure r13 and r14 are preserved, in case we have to restart a - // system call because of a signal (ep has already been set by caller). - st.w r13, PTO+PT_GPR(13)[sp] - st.w r14, PTO+PT_GPR(13)[sp] - mov hilo(ret_from_long_syscall), lp - - MAKE_SYS_CALL // Jump to the syscall function. -END(syscall_long) - -/* Entry point used to return from a long syscall. Only needed to restore - r13/r14 if the general trap mechanism doesnt' do so. */ -L_ENTRY(ret_from_long_syscall): - ld.w PTO+PT_GPR(13)[sp], r13 // Restore the extra registers - ld.w PTO+PT_GPR(13)[sp], r14 - br ret_from_trap // The rest is the same as other traps -END(ret_from_long_syscall) - - -/* These syscalls need access to the struct pt_regs on the stack, so we - implement them in assembly (they're basically all wrappers anyway). */ - -L_ENTRY(sys_fork_wrapper): -#ifdef CONFIG_MMU - addi SIGCHLD, r0, r6 // Arg 0: flags - ld.w PTO+PT_GPR(GPR_SP)[sp], r7 // Arg 1: child SP (use parent's) - movea PTO, sp, r8 // Arg 2: parent context - mov r0, r9 // Arg 3/4/5: 0 - st.w r0, 16[sp] - st.w r0, 20[sp] - mov hilo(CSYM(do_fork)), r18 // Where the real work gets done - br save_extra_state_tramp // Save state and go there -#else - // fork almost works, enough to trick you into looking elsewhere :-( - addi -EINVAL, r0, r10 - jmp [lp] -#endif -END(sys_fork_wrapper) - -L_ENTRY(sys_vfork_wrapper): - addi CLONE_VFORK | CLONE_VM | SIGCHLD, r0, r6 // Arg 0: flags - ld.w PTO+PT_GPR(GPR_SP)[sp], r7 // Arg 1: child SP (use parent's) - movea PTO, sp, r8 // Arg 2: parent context - mov r0, r9 // Arg 3/4/5: 0 - st.w r0, 16[sp] - st.w r0, 20[sp] - mov hilo(CSYM(do_fork)), r18 // Where the real work gets done - br save_extra_state_tramp // Save state and go there -END(sys_vfork_wrapper) - -L_ENTRY(sys_clone_wrapper): - ld.w PTO+PT_GPR(GPR_SP)[sp], r19// parent's stack pointer - cmp r7, r0 // See if child SP arg (arg 1) is 0. - cmov z, r19, r7, r7 // ... and use the parent's if so. - movea PTO, sp, r8 // Arg 2: parent context - mov r0, r9 // Arg 3/4/5: 0 - st.w r0, 16[sp] - st.w r0, 20[sp] - mov hilo(CSYM(do_fork)), r18 // Where the real work gets done - br save_extra_state_tramp // Save state and go there -END(sys_clone_wrapper) - - -L_ENTRY(sys_execve_wrapper): - movea PTO, sp, r9 // add user context as 4th arg - jr CSYM(sys_execve) // Do real work (tail-call). -END(sys_execve_wrapper) - - -L_ENTRY(sys_sigsuspend_wrapper): - movea PTO, sp, r7 // add user context as 2nd arg - mov hilo(CSYM(sys_sigsuspend)), r18 // syscall function - jarl save_extra_state_tramp, lp // Save state and do it - br restore_extra_regs_and_ret_from_trap -END(sys_sigsuspend_wrapper) -L_ENTRY(sys_rt_sigsuspend_wrapper): - movea PTO, sp, r8 // add user context as 3rd arg - mov hilo(CSYM(sys_rt_sigsuspend)), r18 // syscall function - jarl save_extra_state_tramp, lp // Save state and do it - br restore_extra_regs_and_ret_from_trap -END(sys_rt_sigsuspend_wrapper) - -L_ENTRY(sys_sigreturn_wrapper): - movea PTO, sp, r6 // add user context as 1st arg - mov hilo(CSYM(sys_sigreturn)), r18 // syscall function - jarl save_extra_state_tramp, lp // Save state and do it - br restore_extra_regs_and_ret_from_trap -END(sys_sigreturn_wrapper) -L_ENTRY(sys_rt_sigreturn_wrapper): - movea PTO, sp, r6 // add user context as 1st arg - mov hilo(CSYM(sys_rt_sigreturn)), r18// syscall function - jarl save_extra_state_tramp, lp // Save state and do it - br restore_extra_regs_and_ret_from_trap -END(sys_rt_sigreturn_wrapper) - - -/* Save any state not saved by SAVE_STATE(TRAP), and jump to r18. - It's main purpose is to share the rather lengthy code sequence that - SAVE_STATE expands into among the above wrapper functions. */ -L_ENTRY(save_extra_state_tramp): - SAVE_EXTRA_STATE(TRAP) // Save state not saved by entry. - jmp [r18] // Do the work the caller wants -END(save_extra_state_tramp) - - -/* - * Hardware maskable interrupts. - * - * The stack-pointer (r3) should have already been saved to the memory - * location ENTRY_SP (the reason for this is that the interrupt vectors may be - * beyond a 22-bit signed offset jump from the actual interrupt handler, and - * this allows them to save the stack-pointer and use that register to do an - * indirect jump). - */ -G_ENTRY(irq): - SAVE_STATE (IRQ, r0, ENTRY_SP) // Save registers. - - stsr SR_ECR, r6 // Find out which interrupt it was. - movea PTO, sp, r7 // User regs are arg2 - - // All v850 implementations I know about encode their interrupts as - // multiples of 0x10, starting at 0x80 (after NMIs and software - // interrupts). Convert this number into a simple IRQ index for the - // rest of the kernel. We also clear the upper 16 bits, which hold - // NMI info, and don't appear to be cleared when a NMI returns. - shl 16, r6 // clear upper 16 bits - shr 20, r6 // shift back, and remove lower nibble - add -8, r6 // remove bias for irqs - - // Call the high-level interrupt handling code. - jarl CSYM(handle_irq), lp - - RETURN(IRQ) -END(irq) - - -/* - * Debug trap / illegal-instruction exception - * - * The stack-pointer (r3) should have already been saved to the memory - * location ENTRY_SP (the reason for this is that the interrupt vectors may be - * beyond a 22-bit signed offset jump from the actual interrupt handler, and - * this allows them to save the stack-pointer and use that register to do an - * indirect jump). - */ -G_ENTRY(dbtrap): - SAVE_STATE (DBTRAP, r0, ENTRY_SP)// Save registers. - - /* First see if we came from kernel mode; if so, the dbtrap - instruction has a special meaning, to set the DIR (`debug - information register') register. This is because the DIR register - can _only_ be manipulated/read while in `debug mode,' and debug - mode is only active while we're inside the dbtrap handler. The - exact functionality is: { DIR = (DIR | r6) & ~r7; return DIR; }. */ - ld.b PTO+PT_KERNEL_MODE[sp], r19 - cmp r19, r0 - bz 1f - - stsr SR_DIR, r10 - or r6, r10 - not r7, r7 - and r7, r10 - ldsr r10, SR_DIR - stsr SR_DIR, r10 // Confirm the value we set - st.w r10, PTO+PT_GPR(10)[sp] // return it - br 3f - -1: ei // Enable interrupts. - - /* The default signal type we raise. */ - mov SIGTRAP, r6 - - /* See if it's a single-step trap. */ - stsr SR_DBPSW, r19 - andi 0x0800, r19, r19 - bnz 2f - - /* Look to see if the preceding instruction was is a dbtrap or not, - to decide which signal we should use. */ - stsr SR_DBPC, r19 // PC following trapping insn - ld.hu -2[r19], r19 - ori 0xf840, r0, r20 // DBTRAP insn - cmp r19, r20 // Was this trap caused by DBTRAP? - cmov ne, SIGILL, r6, r6 // Choose signal appropriately - - /* Raise the desired signal. */ -2: mov CURRENT_TASK, r7 // Arg 1: task - jarl CSYM(send_sig), lp // tail call - -3: RETURN(DBTRAP) -END(dbtrap) - - -/* - * Hardware non-maskable interrupts. - * - * The stack-pointer (r3) should have already been saved to the memory - * location ENTRY_SP (the reason for this is that the interrupt vectors may be - * beyond a 22-bit signed offset jump from the actual interrupt handler, and - * this allows them to save the stack-pointer and use that register to do an - * indirect jump). - */ -G_ENTRY(nmi): - SAVE_STATE (NMI, r0, NMI_ENTRY_SP); /* Save registers. */ - - stsr SR_ECR, r6; /* Find out which nmi it was. */ - shr 20, r6; /* Extract NMI code in bits 20-24. */ - movea PTO, sp, r7; /* User regs are arg2. */ - - /* Non-maskable interrupts always lie right after maskable interrupts. - Call the generic IRQ handler, with two arguments, the IRQ number, - and a pointer to the user registers, to handle the specifics. - (we subtract one because the first NMI has code 1). */ - addi FIRST_NMI - 1, r6, r6 - jarl CSYM(handle_irq), lp - - RETURN(NMI) -END(nmi) - - -/* - * Trap with no handler - */ -L_ENTRY(bad_trap_wrapper): - mov r19, r6 // Arg 0: trap number - movea PTO, sp, r7 // Arg 1: user regs - jr CSYM(bad_trap) // tail call handler -END(bad_trap_wrapper) - - -/* - * Invoke the scheduler, called from the trap/irq kernel exit path. - * - * This basically just calls `schedule', but also arranges for extra - * registers to be saved for ptrace'd processes, so ptrace can modify them. - */ -L_ENTRY(call_scheduler): - ld.w TASK_PTRACE[CURRENT_TASK], r19 // See if task is ptrace'd - cmp r19, r0 - bnz 1f // ... yes, do special stuff - jr CSYM(schedule) // ... no, just tail-call scheduler - - // Save extra regs for ptrace'd task. We want to save anything - // that would otherwise only be `implicitly' saved by the normal - // compiler calling-convention. -1: mov sp, ep // Setup EP for SAVE_CALL_SAVED_REGS - SAVE_CALL_SAVED_REGS // Save call-saved registers to stack - mov lp, r20 // Save LP in a callee-saved register - - jarl CSYM(schedule), lp // Call scheduler - - mov r20, lp - mov sp, ep // We can't rely on EP after return - RESTORE_CALL_SAVED_REGS // Restore (possibly modified) regs - jmp [lp] // Return to the return path -END(call_scheduler) - - -/* - * This is an out-of-line handler for two special cases during the kernel - * trap/irq exit sequence: - * - * (1) If r18 is non-zero then a signal needs to be handled, which is - * done, and then the caller returned to. - * - * (2) If r18 is non-zero then we're returning to a ptraced process, which - * has several special cases -- single-stepping and trap tracing, both - * of which require using the `dbret' instruction to exit the kernel - * instead of the normal `reti' (this is because the CPU not correctly - * single-step after a reti). In this case, of course, this handler - * never returns to the caller. - * - * In either case, all registers should have been saved to the current - * state-save-frame on the stack, except for callee-saved registers. - * - * [These two different cases are combined merely to avoid bloating the - * macro-inlined code, not because they really make much sense together!] - */ -L_ENTRY(handle_signal_or_ptrace_return): - cmp r18, r0 // See if handling a signal - bz 1f // ... nope, go do ptrace return - - // Handle a signal - mov lp, r20 // Save link-pointer - mov r10, r21 // Save return-values (for trap) - mov r11, r22 - - movea PTO, sp, r6 // Arg 1: struct pt_regs *regs - mov r0, r7 // Arg 2: sigset_t *oldset - jarl CSYM(do_signal), lp // Handle the signal - di // sig handling enables interrupts - - mov r20, lp // Restore link-pointer - mov r21, r10 // Restore return-values (for trap) - mov r22, r11 - ld.w TASK_PTRACE[CURRENT_TASK], r19 // check ptrace flags too - cmp r19, r0 - bnz 1f // ... some set, so look more -2: jmp [lp] // ... none set, so return normally - - // ptrace return -1: ld.w PTO+PT_PSW[sp], r19 // Look at user-processes's flags - andi 0x0800, r19, r19 // See if single-step flag is set - bz 2b // ... nope, return normally - - // Return as if from a dbtrap insn - st.b r0, KM // Now officially in user state. - POP_STATE(DBTRAP) // Restore regs - st.w sp, KSP // Save the kernel stack pointer. - ld.w PT_GPR(GPR_SP)-PT_SIZE[sp], sp // Restore user stack pointer. - DBTRAP_RET // Return from the trap/interrupt. -END(handle_signal_or_ptrace_return) - - -/* - * This is where we switch between two threads. The arguments are: - * r6 -- pointer to the struct thread for the `current' process - * r7 -- pointer to the struct thread for the `new' process. - * when this function returns, it will return to the new thread. - */ -C_ENTRY(switch_thread): - // Return the previous task (r10 is not clobbered by restore below) - mov CURRENT_TASK, r10 - // First, push the current processor state on the stack - PUSH_STATE(SWITCH) - // Now save the location of the kernel stack pointer for this thread; - // since we've pushed all other state on the stack, this is enough to - // restore it all later. - st.w sp, THREAD_KSP[r6] - // Now restore the stack pointer from the new process - ld.w THREAD_KSP[r7], sp - // ... and restore all state from that - POP_STATE(SWITCH) - // Update the current task pointer - GET_CURRENT_TASK(CURRENT_TASK) - // Now return into the new thread - jmp [lp] -C_END(switch_thread) - - - .data - - .align 4 -C_DATA(trap_table): - .long bad_trap_wrapper // trap 0, doesn't use trap table. - .long syscall_long // trap 1, `long' syscall. - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper - .long bad_trap_wrapper -C_END(trap_table) - - - .section .rodata - - .align 4 -C_DATA(sys_call_table): - .long CSYM(sys_restart_syscall) // 0 - .long CSYM(sys_exit) - .long sys_fork_wrapper - .long CSYM(sys_read) - .long CSYM(sys_write) - .long CSYM(sys_open) // 5 - .long CSYM(sys_close) - .long CSYM(sys_waitpid) - .long CSYM(sys_creat) - .long CSYM(sys_link) - .long CSYM(sys_unlink) // 10 - .long sys_execve_wrapper - .long CSYM(sys_chdir) - .long CSYM(sys_time) - .long CSYM(sys_mknod) - .long CSYM(sys_chmod) // 15 - .long CSYM(sys_chown) - .long CSYM(sys_ni_syscall) // was: break - .long CSYM(sys_ni_syscall) // was: oldstat (aka stat) - .long CSYM(sys_lseek) - .long CSYM(sys_getpid) // 20 - .long CSYM(sys_mount) - .long CSYM(sys_oldumount) - .long CSYM(sys_setuid) - .long CSYM(sys_getuid) - .long CSYM(sys_stime) // 25 - .long CSYM(sys_ptrace) - .long CSYM(sys_alarm) - .long CSYM(sys_ni_syscall) // was: oldfstat (aka fstat) - .long CSYM(sys_pause) - .long CSYM(sys_utime) // 30 - .long CSYM(sys_ni_syscall) // was: stty - .long CSYM(sys_ni_syscall) // was: gtty - .long CSYM(sys_access) - .long CSYM(sys_nice) - .long CSYM(sys_ni_syscall) // 35, was: ftime - .long CSYM(sys_sync) - .long CSYM(sys_kill) - .long CSYM(sys_rename) - .long CSYM(sys_mkdir) - .long CSYM(sys_rmdir) // 40 - .long CSYM(sys_dup) - .long CSYM(sys_pipe) - .long CSYM(sys_times) - .long CSYM(sys_ni_syscall) // was: prof - .long CSYM(sys_brk) // 45 - .long CSYM(sys_setgid) - .long CSYM(sys_getgid) - .long CSYM(sys_signal) - .long CSYM(sys_geteuid) - .long CSYM(sys_getegid) // 50 - .long CSYM(sys_acct) - .long CSYM(sys_umount) // recycled never used phys() - .long CSYM(sys_ni_syscall) // was: lock - .long CSYM(sys_ioctl) - .long CSYM(sys_fcntl) // 55 - .long CSYM(sys_ni_syscall) // was: mpx - .long CSYM(sys_setpgid) - .long CSYM(sys_ni_syscall) // was: ulimit - .long CSYM(sys_ni_syscall) - .long CSYM(sys_umask) // 60 - .long CSYM(sys_chroot) - .long CSYM(sys_ustat) - .long CSYM(sys_dup2) - .long CSYM(sys_getppid) - .long CSYM(sys_getpgrp) // 65 - .long CSYM(sys_setsid) - .long CSYM(sys_sigaction) - .long CSYM(sys_sgetmask) - .long CSYM(sys_ssetmask) - .long CSYM(sys_setreuid) // 70 - .long CSYM(sys_setregid) - .long sys_sigsuspend_wrapper - .long CSYM(sys_sigpending) - .long CSYM(sys_sethostname) - .long CSYM(sys_setrlimit) // 75 - .long CSYM(sys_getrlimit) - .long CSYM(sys_getrusage) - .long CSYM(sys_gettimeofday) - .long CSYM(sys_settimeofday) - .long CSYM(sys_getgroups) // 80 - .long CSYM(sys_setgroups) - .long CSYM(sys_select) - .long CSYM(sys_symlink) - .long CSYM(sys_ni_syscall) // was: oldlstat (aka lstat) - .long CSYM(sys_readlink) // 85 - .long CSYM(sys_uselib) - .long CSYM(sys_swapon) - .long CSYM(sys_reboot) - .long CSYM(old_readdir) - .long CSYM(sys_mmap) // 90 - .long CSYM(sys_munmap) - .long CSYM(sys_truncate) - .long CSYM(sys_ftruncate) - .long CSYM(sys_fchmod) - .long CSYM(sys_fchown) // 95 - .long CSYM(sys_getpriority) - .long CSYM(sys_setpriority) - .long CSYM(sys_ni_syscall) // was: profil - .long CSYM(sys_statfs) - .long CSYM(sys_fstatfs) // 100 - .long CSYM(sys_ni_syscall) // i386: ioperm - .long CSYM(sys_socketcall) - .long CSYM(sys_syslog) - .long CSYM(sys_setitimer) - .long CSYM(sys_getitimer) // 105 - .long CSYM(sys_newstat) - .long CSYM(sys_newlstat) - .long CSYM(sys_newfstat) - .long CSYM(sys_ni_syscall) // was: olduname (aka uname) - .long CSYM(sys_ni_syscall) // 110, i386: iopl - .long CSYM(sys_vhangup) - .long CSYM(sys_ni_syscall) // was: idle - .long CSYM(sys_ni_syscall) // i386: vm86old - .long CSYM(sys_wait4) - .long CSYM(sys_swapoff) // 115 - .long CSYM(sys_sysinfo) - .long CSYM(sys_ipc) - .long CSYM(sys_fsync) - .long sys_sigreturn_wrapper - .long sys_clone_wrapper // 120 - .long CSYM(sys_setdomainname) - .long CSYM(sys_newuname) - .long CSYM(sys_ni_syscall) // i386: modify_ldt, m68k: cacheflush - .long CSYM(sys_adjtimex) - .long CSYM(sys_ni_syscall) // 125 - sys_mprotect - .long CSYM(sys_sigprocmask) - .long CSYM(sys_ni_syscall) // sys_create_module - .long CSYM(sys_init_module) - .long CSYM(sys_delete_module) - .long CSYM(sys_ni_syscall) // 130 - sys_get_kernel_syms - .long CSYM(sys_quotactl) - .long CSYM(sys_getpgid) - .long CSYM(sys_fchdir) - .long CSYM(sys_bdflush) - .long CSYM(sys_sysfs) // 135 - .long CSYM(sys_personality) - .long CSYM(sys_ni_syscall) // for afs_syscall - .long CSYM(sys_setfsuid) - .long CSYM(sys_setfsgid) - .long CSYM(sys_llseek) // 140 - .long CSYM(sys_getdents) - .long CSYM(sys_select) // for backward compat; remove someday - .long CSYM(sys_flock) - .long CSYM(sys_ni_syscall) // sys_msync - .long CSYM(sys_readv) // 145 - .long CSYM(sys_writev) - .long CSYM(sys_getsid) - .long CSYM(sys_fdatasync) - .long CSYM(sys_sysctl) - .long CSYM(sys_ni_syscall) // 150 - sys_mlock - .long CSYM(sys_ni_syscall) // sys_munlock - .long CSYM(sys_ni_syscall) // sys_mlockall - .long CSYM(sys_ni_syscall) // sys_munlockall - .long CSYM(sys_sched_setparam) - .long CSYM(sys_sched_getparam) // 155 - .long CSYM(sys_sched_setscheduler) - .long CSYM(sys_sched_getscheduler) - .long CSYM(sys_sched_yield) - .long CSYM(sys_sched_get_priority_max) - .long CSYM(sys_sched_get_priority_min) // 160 - .long CSYM(sys_sched_rr_get_interval) - .long CSYM(sys_nanosleep) - .long CSYM(sys_ni_syscall) // sys_mremap - .long CSYM(sys_setresuid) - .long CSYM(sys_getresuid) // 165 - .long CSYM(sys_ni_syscall) // for vm86 - .long CSYM(sys_ni_syscall) // sys_query_module - .long CSYM(sys_poll) - .long CSYM(sys_nfsservctl) - .long CSYM(sys_setresgid) // 170 - .long CSYM(sys_getresgid) - .long CSYM(sys_prctl) - .long sys_rt_sigreturn_wrapper - .long CSYM(sys_rt_sigaction) - .long CSYM(sys_rt_sigprocmask) // 175 - .long CSYM(sys_rt_sigpending) - .long CSYM(sys_rt_sigtimedwait) - .long CSYM(sys_rt_sigqueueinfo) - .long sys_rt_sigsuspend_wrapper - .long CSYM(sys_pread64) // 180 - .long CSYM(sys_pwrite64) - .long CSYM(sys_lchown) - .long CSYM(sys_getcwd) - .long CSYM(sys_capget) - .long CSYM(sys_capset) // 185 - .long CSYM(sys_sigaltstack) - .long CSYM(sys_sendfile) - .long CSYM(sys_ni_syscall) // streams1 - .long CSYM(sys_ni_syscall) // streams2 - .long sys_vfork_wrapper // 190 - .long CSYM(sys_ni_syscall) - .long CSYM(sys_mmap2) - .long CSYM(sys_truncate64) - .long CSYM(sys_ftruncate64) - .long CSYM(sys_stat64) // 195 - .long CSYM(sys_lstat64) - .long CSYM(sys_fstat64) - .long CSYM(sys_fcntl64) - .long CSYM(sys_getdents64) - .long CSYM(sys_pivot_root) // 200 - .long CSYM(sys_gettid) - .long CSYM(sys_tkill) -sys_call_table_end: -C_END(sys_call_table) diff --git a/arch/v850/kernel/fpga85e2c.c b/arch/v850/kernel/fpga85e2c.c deleted file mode 100644 index ab9cf16a85c8..000000000000 --- a/arch/v850/kernel/fpga85e2c.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * arch/v850/kernel/fpga85e2c.h -- Machine-dependent defs for - * FPGA implementation of V850E2/NA85E2C - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "mach.h" - -extern void memcons_setup (void); - - -#define REG_DUMP_ADDR 0x220000 - - -extern struct irqaction reg_snap_action; /* fwd decl */ - - -void __init mach_early_init (void) -{ - int i; - const u32 *src; - register u32 *dst asm ("ep"); - extern u32 _intv_end, _intv_load_start; - - /* Set bus sizes: CS0 32-bit, CS1 16-bit, CS7 8-bit, - everything else 32-bit. */ - V850E2_BSC = 0x2AA6; - for (i = 2; i <= 6; i++) - CSDEV(i) = 0; /* 32 bit */ - - /* Ensure that the simulator halts on a panic, instead of going - into an infinite loop inside the panic function. */ - panic_timeout = -1; - - /* Move the interrupt vectors into their real location. Note that - any relocations there are relative to the real location, so we - don't have to fix anything up. We use a loop instead of calling - memcpy to keep this a leaf function (to avoid a function - prologue being generated). */ - dst = 0x10; /* &_intv_start + 0x10. */ - src = &_intv_load_start; - do { - u32 t0 = src[0], t1 = src[1], t2 = src[2], t3 = src[3]; - u32 t4 = src[4], t5 = src[5], t6 = src[6], t7 = src[7]; - dst[0] = t0; dst[1] = t1; dst[2] = t2; dst[3] = t3; - dst[4] = t4; dst[5] = t5; dst[6] = t6; dst[7] = t7; - dst += 8; - src += 8; - } while (dst < &_intv_end); -} - -void __init mach_setup (char **cmdline) -{ - memcons_setup (); - - /* Setup up NMI0 to copy the registers to a known memory location. - The FGPA board has a button that produces NMI0 when pressed, so - this allows us to push the button, and then look at memory to see - what's in the registers (there's no other way to easily do so). - We have to use `setup_irq' instead of `request_irq' because it's - still too early to do memory allocation. */ - setup_irq (IRQ_NMI (0), ®_snap_action); -} - -void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len) -{ - *ram_start = ERAM_ADDR; - *ram_len = ERAM_SIZE; -} - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Setup up the timer interrupt. The FPGA peripheral control - registers _only_ work with single-bit writes (set1/clr1)! */ - __clear_bit (RPU_GTMC_CE_BIT, &RPU_GTMC); - __clear_bit (RPU_GTMC_CLK_BIT, &RPU_GTMC); - __set_bit (RPU_GTMC_CE_BIT, &RPU_GTMC); - - /* We use the first RPU interrupt, which occurs every 8.192ms. */ - setup_irq (IRQ_RPU (0), timer_action); -} - - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -void machine_halt (void) __attribute__ ((noreturn)); -void machine_halt (void) -{ - for (;;) { - DWC(0) = 0x7777; - DWC(1) = 0x7777; - ASC = 0xffff; - FLGREG(0) = 1; /* Halt immediately. */ - asm ("di; halt; nop; nop; nop; nop; nop"); - } -} - -void machine_restart (char *__unused) -{ - machine_halt (); -} - -void machine_power_off (void) -{ - machine_halt (); -} - - -/* Interrupts */ - -struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_MACH_IRQS, 1, 7 }, - { "RPU", IRQ_RPU(0), IRQ_RPU_NUM, 1, 6 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -/* Initialize interrupts. */ -void __init mach_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - - -/* An interrupt handler that copies the registers to a known memory location, - for debugging purposes. */ - -static void make_reg_snap (int irq, void *dummy, struct pt_regs *regs) -{ - (*(unsigned *)REG_DUMP_ADDR)++; - (*(struct pt_regs *)(REG_DUMP_ADDR + sizeof (unsigned))) = *regs; -} - -static int reg_snap_dev_id; -static struct irqaction reg_snap_action = { - .handler = make_reg_snap, - .mask = CPU_MASK_NONE, - .name = "reg_snap", - .dev_id = ®_snap_dev_id, -}; diff --git a/arch/v850/kernel/fpga85e2c.ld b/arch/v850/kernel/fpga85e2c.ld deleted file mode 100644 index b5d4578ae411..000000000000 --- a/arch/v850/kernel/fpga85e2c.ld +++ /dev/null @@ -1,62 +0,0 @@ -/* Linker script for the FPGA implementation of the V850E2 NA85E2C cpu core - (CONFIG_V850E2_FPGA85E2C). */ - -MEMORY { - /* Reset vector. */ - RESET : ORIGIN = 0, LENGTH = 0x10 - /* Interrupt vectors. */ - INTV : ORIGIN = 0x10, LENGTH = 0x470 - /* The `window' in RAM were we're allowed to load stuff. */ - RAM_LOW : ORIGIN = 0x480, LENGTH = 0x0005FB80 - /* Some more ram above the window were we can put bss &c. */ - RAM_HIGH : ORIGIN = 0x00060000, LENGTH = 0x000A0000 - /* This is the area visible from the outside world (we can use - this only for uninitialized data). */ - VISIBLE : ORIGIN = 0x00200000, LENGTH = 0x00060000 -} - -SECTIONS { - .reset : { - __kram_start = . ; - __intv_start = . ; - *(.intv.reset) /* Reset vector */ - } > RESET - - .ram_low : { - __r0_ram = . ; /* Must be near address 0. */ - . = . + 32 ; - - TEXT_CONTENTS - DATA_CONTENTS - ROOT_FS_CONTENTS - RAMK_INIT_CONTENTS_NO_END - INITRAMFS_CONTENTS - } > RAM_LOW - - /* Where the interrupt vectors are initially loaded. */ - __intv_load_start = . ; - - .intv : { - *(.intv.common) /* Vectors common to all v850e proc. */ - *(.intv.mach) /* Machine-specific int. vectors. */ - __intv_end = . ; - } > INTV AT> RAM_LOW - - .ram_high : { - /* This is here so that when we free init memory the - load-time copy of the interrupt vectors and any empty - space at the end of the `RAM_LOW' area is freed too. */ - . = ALIGN (4096); - __init_end = . ; - - BSS_CONTENTS - __kram_end = . ; - BOOTMAP_CONTENTS - } > RAM_HIGH - - .visible : { - _memcons_output = . ; - . = . + 0x8000 ; - _memcons_output_end = . ; - } > VISIBLE -} diff --git a/arch/v850/kernel/gbus_int.c b/arch/v850/kernel/gbus_int.c deleted file mode 100644 index b2bcc251f65b..000000000000 --- a/arch/v850/kernel/gbus_int.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * arch/v850/kernel/gbus_int.c -- Midas labs GBUS interrupt support - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include - - -/* The number of shared GINT interrupts. */ -#define NUM_GINTS 4 - -/* For each GINT interrupt, how many GBUS interrupts are using it. */ -static unsigned gint_num_active_irqs[NUM_GINTS] = { 0 }; - -/* A table of GINTn interrupts we actually use. - Note that we don't use GINT0 because all the boards we support treat it - specially. */ -struct used_gint { - unsigned gint; - unsigned priority; -} used_gint[] = { - { 1, GBUS_INT_PRIORITY_HIGH }, - { 3, GBUS_INT_PRIORITY_LOW } -}; -#define NUM_USED_GINTS ARRAY_SIZE(used_gint) - -/* A table of which GINT is used by each GBUS interrupts (they are - assigned based on priority). */ -static unsigned char gbus_int_gint[IRQ_GBUS_INT_NUM]; - - -/* Interrupt enabling/disabling. */ - -/* Enable interrupt handling for interrupt IRQ. */ -void gbus_int_enable_irq (unsigned irq) -{ - unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ]; - GBUS_INT_ENABLE (GBUS_INT_IRQ_WORD(irq), gint) - |= GBUS_INT_IRQ_MASK (irq); -} - -/* Disable interrupt handling for interrupt IRQ. Note that any - interrupts received while disabled will be delivered once the - interrupt is enabled again, unless they are explicitly cleared using - `gbus_int_clear_pending_irq'. */ -void gbus_int_disable_irq (unsigned irq) -{ - unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ]; - GBUS_INT_ENABLE (GBUS_INT_IRQ_WORD(irq), gint) - &= ~GBUS_INT_IRQ_MASK (irq); -} - -/* Return true if interrupt handling for interrupt IRQ is enabled. */ -int gbus_int_irq_enabled (unsigned irq) -{ - unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ]; - return (GBUS_INT_ENABLE (GBUS_INT_IRQ_WORD(irq), gint) - & GBUS_INT_IRQ_MASK(irq)); -} - -/* Disable all GBUS irqs. */ -void gbus_int_disable_irqs () -{ - unsigned w, n; - for (w = 0; w < GBUS_INT_NUM_WORDS; w++) - for (n = 0; n < IRQ_GINT_NUM; n++) - GBUS_INT_ENABLE (w, n) = 0; -} - -/* Clear any pending interrupts for IRQ. */ -void gbus_int_clear_pending_irq (unsigned irq) -{ - GBUS_INT_CLEAR (GBUS_INT_IRQ_WORD(irq)) = GBUS_INT_IRQ_MASK (irq); -} - -/* Return true if interrupt IRQ is pending (but disabled). */ -int gbus_int_irq_pending (unsigned irq) -{ - return (GBUS_INT_STATUS (GBUS_INT_IRQ_WORD(irq)) - & GBUS_INT_IRQ_MASK(irq)); -} - - -/* Delegating interrupts. */ - -/* Handle a shared GINT interrupt by passing to the appropriate GBUS - interrupt handler. */ -static irqreturn_t gbus_int_handle_irq (int irq, void *dev_id, - struct pt_regs *regs) -{ - unsigned w; - irqreturn_t rval = IRQ_NONE; - unsigned gint = irq - IRQ_GINT (0); - - for (w = 0; w < GBUS_INT_NUM_WORDS; w++) { - unsigned status = GBUS_INT_STATUS (w); - unsigned enable = GBUS_INT_ENABLE (w, gint); - - /* Only pay attention to enabled interrupts. */ - status &= enable; - if (status) { - irq = IRQ_GBUS_INT (w * GBUS_INT_BITS_PER_WORD); - do { - /* There's an active interrupt in word - W, find out which one, and call its - handler. */ - - while (! (status & 0x1)) { - irq++; - status >>= 1; - } - status &= ~0x1; - - /* Recursively call handle_irq to handle it. */ - handle_irq (irq, regs); - rval = IRQ_HANDLED; - } while (status); - } - } - - /* Toggle the `all enable' bit back and forth, which should cause - another edge transition if there are any other interrupts - still pending, and so result in another CPU interrupt. */ - GBUS_INT_ENABLE (0, gint) &= ~0x1; - GBUS_INT_ENABLE (0, gint) |= 0x1; - - return rval; -} - - -/* Initialize GBUS interrupt sources. */ - -static void irq_nop (unsigned irq) { } - -static unsigned gbus_int_startup_irq (unsigned irq) -{ - unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ]; - - if (gint_num_active_irqs[gint] == 0) { - /* First enable the CPU interrupt. */ - int rval = - request_irq (IRQ_GINT(gint), gbus_int_handle_irq, - IRQF_DISABLED, - "gbus_int_handler", - &gint_num_active_irqs[gint]); - if (rval != 0) - return rval; - } - - gint_num_active_irqs[gint]++; - - gbus_int_clear_pending_irq (irq); - gbus_int_enable_irq (irq); - - return 0; -} - -static void gbus_int_shutdown_irq (unsigned irq) -{ - unsigned gint = gbus_int_gint[irq - GBUS_INT_BASE_IRQ]; - - gbus_int_disable_irq (irq); - - if (--gint_num_active_irqs[gint] == 0) - /* Disable the CPU interrupt. */ - free_irq (IRQ_GINT(gint), &gint_num_active_irqs[gint]); -} - -/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array - INITS (which is terminated by an entry with the name field == 0). */ -void __init gbus_int_init_irq_types (struct gbus_int_irq_init *inits, - struct hw_interrupt_type *hw_irq_types) -{ - struct gbus_int_irq_init *init; - for (init = inits; init->name; init++) { - unsigned i; - struct hw_interrupt_type *hwit = hw_irq_types++; - - hwit->typename = init->name; - - hwit->startup = gbus_int_startup_irq; - hwit->shutdown = gbus_int_shutdown_irq; - hwit->enable = gbus_int_enable_irq; - hwit->disable = gbus_int_disable_irq; - hwit->ack = irq_nop; - hwit->end = irq_nop; - - /* Initialize kernel IRQ infrastructure for this interrupt. */ - init_irq_handlers(init->base, init->num, init->interval, hwit); - - /* Set the interrupt priorities. */ - for (i = 0; i < init->num; i++) { - unsigned j; - for (j = 0; j < NUM_USED_GINTS; j++) - if (used_gint[j].priority > init->priority) - break; - /* Wherever we stopped looking is one past the - GINT we want. */ - gbus_int_gint[init->base + i * init->interval - - GBUS_INT_BASE_IRQ] - = used_gint[j > 0 ? j - 1 : 0].gint; - } - } -} - - -/* Initialize IRQS. */ - -/* Chip interrupts (GINTn) shared among GBUS interrupts. */ -static struct hw_interrupt_type gint_hw_itypes[NUM_USED_GINTS]; - - -/* GBUS interrupts themselves. */ - -struct gbus_int_irq_init gbus_irq_inits[] __initdata = { - /* First set defaults. */ - { "GBUS_INT", IRQ_GBUS_INT(0), IRQ_GBUS_INT_NUM, 1, 6}, - { 0 } -}; -#define NUM_GBUS_IRQ_INITS (ARRAY_SIZE(gbus_irq_inits) - 1) - -static struct hw_interrupt_type gbus_hw_itypes[NUM_GBUS_IRQ_INITS]; - - -/* Initialize GBUS interrupts. */ -void __init gbus_int_init_irqs (void) -{ - unsigned i; - - /* First initialize the shared gint interrupts. */ - for (i = 0; i < NUM_USED_GINTS; i++) { - unsigned gint = used_gint[i].gint; - struct v850e_intc_irq_init gint_irq_init[2]; - - /* We initialize one GINT interrupt at a time. */ - gint_irq_init[0].name = "GINT"; - gint_irq_init[0].base = IRQ_GINT (gint); - gint_irq_init[0].num = 1; - gint_irq_init[0].interval = 1; - gint_irq_init[0].priority = used_gint[i].priority; - - gint_irq_init[1].name = 0; /* Terminate the vector. */ - - v850e_intc_init_irq_types (gint_irq_init, gint_hw_itypes); - } - - /* Then the GBUS interrupts. */ - gbus_int_disable_irqs (); - gbus_int_init_irq_types (gbus_irq_inits, gbus_hw_itypes); - /* Turn on the `all enable' bits, which are ANDed with - individual interrupt enable bits; we only want to bother with - the latter. They are the first bit in the first word of each - interrupt-enable area. */ - for (i = 0; i < NUM_USED_GINTS; i++) - GBUS_INT_ENABLE (0, used_gint[i].gint) = 0x1; -} diff --git a/arch/v850/kernel/head.S b/arch/v850/kernel/head.S deleted file mode 100644 index c490b937ef14..000000000000 --- a/arch/v850/kernel/head.S +++ /dev/null @@ -1,128 +0,0 @@ -/* - * arch/v850/kernel/head.S -- Lowest-level startup code - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include - - -/* Make a slightly more convenient alias for C_SYMBOL_NAME. */ -#define CSYM C_SYMBOL_NAME - - - .text - - // Define `mach_early_init' as a weak symbol - .global CSYM(mach_early_init) - .weak CSYM(mach_early_init) - -C_ENTRY(start): - // Make sure interrupts are turned off, just in case - di - -#ifdef CONFIG_RESET_GUARD - // See if we got here via an unexpected reset - ld.w RESET_GUARD, r19 // Check current value of reset guard - mov RESET_GUARD_ACTIVE, r20 - cmp r19, r20 - bne 1f // Guard was not active - - // If we get here, the reset guard was active. Load up some - // interesting values as arguments, and jump to the handler. - st.w r0, RESET_GUARD // Allow further resets to succeed - mov lp, r6 // Arg 0: return address - ld.b KM, r7 // Arg 1: kernel mode - mov sp, r9 // Arg 3: stack pointer - ld.w KSP, r19 // maybe switch to kernel stack - cmp r7, r0 // see if already in kernel mode - cmov z, r19, sp, sp // and switch to kernel stack if not - GET_CURRENT_TASK(r8) // Arg 2: task pointer - jr CSYM(unexpected_reset) - -1: st.w r20, RESET_GUARD // Turn on reset guard -#endif /* CONFIG_RESET_GUARD */ - - // Setup a temporary stack for doing pre-initialization function calls. - // - // We can't use the initial kernel stack, because (1) it may be - // located in memory we're not allowed to touch, and (2) since - // it's in the data segment, calling memcpy to initialize that - // area from ROM will overwrite memcpy's return address. - mov hilo(CSYM(_init_stack_end) - 4), sp - - // See if there's a platform-specific early-initialization routine - // defined; it's a weak symbol, so it will have an address of zero if - // there's not. - mov hilo(CSYM(mach_early_init)), r6 - cmp r6, r0 - bz 3f - - // There is one, so call it. If this function is written in C, it - // should be very careful -- the stack pointer is valid, but very - // little else is (e.g., bss is not zeroed yet, and initialized data - // hasn't been). - jarl 2f, lp // first figure out return address -2: add 3f - ., lp - jmp [r6] // do call -3: - -#ifdef CONFIG_ROM_KERNEL - // Copy the data area from ROM to RAM - mov hilo(CSYM(_rom_copy_dst_start)), r6 - mov hilo(CSYM(_rom_copy_src_start)), r7 - mov hilo(CSYM(_rom_copy_dst_end)), r8 - sub r6, r8 - jarl CSYM(memcpy), lp -#endif - - // Load the initial thread's stack, and current task pointer (in r16) - mov hilo(CSYM(init_thread_union)), r19 - movea THREAD_SIZE, r19, sp - ld.w TI_TASK[r19], CURRENT_TASK - -#ifdef CONFIG_TIME_BOOTUP - /* This stuff must come after mach_early_init, because interrupts may - not work until after its been called. */ - jarl CSYM(highres_timer_reset), lp - jarl CSYM(highres_timer_start), lp -#endif - - // Kernel stack pointer save location - st.w sp, KSP - - // Assert that we're in `kernel mode' - mov 1, r19 - st.w r19, KM - -#ifdef CONFIG_ZERO_BSS - // Zero bss area, since we can't rely upon any loader to do so - mov hilo(CSYM(_sbss)), r6 - mov r0, r7 - mov hilo(CSYM(_ebss)), r8 - sub r6, r8 - jarl CSYM(memset), lp -#endif - - // What happens if the main kernel function returns (it shouldn't) - mov hilo(CSYM(machine_halt)), lp - - // Start the linux kernel. We use an indirect jump to get extra - // range, because on some platforms this initial startup code - // (and the associated platform-specific code in mach_early_init) - // are located far away from the main kernel, e.g. so that they - // can initialize RAM first and copy the kernel or something. - mov hilo(CSYM(start_kernel)), r12 - jmp [r12] -C_END(start) diff --git a/arch/v850/kernel/highres_timer.c b/arch/v850/kernel/highres_timer.c deleted file mode 100644 index b16ad1eaf966..000000000000 --- a/arch/v850/kernel/highres_timer.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * arch/v850/kernel/highres_timer.c -- High resolution timing routines - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include - -#define HIGHRES_TIMER_USEC_SHIFT 12 - -/* Pre-calculated constant used for converting ticks to real time - units. We initialize it to prevent it being put into BSS. */ -static u32 highres_timer_usec_prescale = 1; - -void highres_timer_slow_tick_irq (void) __attribute__ ((noreturn)); -void highres_timer_slow_tick_irq (void) -{ - /* This is an interrupt handler, so it must be very careful to - not to trash any registers. At this point, the stack-pointer - (r3) has been saved in the chip ram location ENTRY_SP by the - interrupt vector, so we can use it as a scratch register; we - must also restore it before returning. */ - asm ("ld.w %0[r0], sp;" - "add 1, sp;" - "st.w sp, %0[r0];" - "ld.w %1[r0], sp;" /* restore pre-irq stack-pointer */ - "reti" - :: - "i" (HIGHRES_TIMER_SLOW_TICKS_ADDR), - "i" (ENTRY_SP_ADDR) - : "memory"); -} - -void highres_timer_reset (void) -{ - V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT) = 0; - HIGHRES_TIMER_SLOW_TICKS = 0; -} - -void highres_timer_start (void) -{ - u32 fast_tick_rate; - - /* Start hardware timer. */ - v850e_timer_d_configure (HIGHRES_TIMER_TIMER_D_UNIT, - HIGHRES_TIMER_SLOW_TICK_RATE); - - fast_tick_rate = - (V850E_TIMER_D_BASE_FREQ - >> V850E_TIMER_D_DIVLOG2 (HIGHRES_TIMER_TIMER_D_UNIT)); - - /* The obvious way of calculating microseconds from fast ticks - is to do: - - usec = fast_ticks * 10^6 / fast_tick_rate - - However, divisions are much slower than multiplications, and - the above calculation can overflow, so we do this instead: - - usec = fast_ticks * (10^6 * 2^12 / fast_tick_rate) / 2^12 - - since we can pre-calculate (10^6 * (2^12 / fast_tick_rate)) - and use a shift for dividing by 2^12, this avoids division, - and is almost as accurate (it differs by about 2 microseconds - at the extreme value of the fast-tick counter's ranger). */ - highres_timer_usec_prescale = ((1000000 << HIGHRES_TIMER_USEC_SHIFT) - / fast_tick_rate); - - /* Enable the interrupt (which is hardwired to this use), and - give it the highest priority. */ - V850E_INTC_IC (IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT)) = 0; -} - -void highres_timer_stop (void) -{ - /* Stop the timer. */ - V850E_TIMER_D_TMCD (HIGHRES_TIMER_TIMER_D_UNIT) = - V850E_TIMER_D_TMCD_CAE; - /* Disable its interrupt, just in case. */ - v850e_intc_disable_irq (IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT)); -} - -inline void highres_timer_read_ticks (u32 *slow_ticks, u32 *fast_ticks) -{ - int flags; - u32 fast_ticks_1, fast_ticks_2, _slow_ticks; - - local_irq_save (flags); - fast_ticks_1 = V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT); - _slow_ticks = HIGHRES_TIMER_SLOW_TICKS; - fast_ticks_2 = V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT); - local_irq_restore (flags); - - if (fast_ticks_2 < fast_ticks_1) - _slow_ticks++; - - *slow_ticks = _slow_ticks; - *fast_ticks = fast_ticks_2; -} - -inline void highres_timer_ticks_to_timeval (u32 slow_ticks, u32 fast_ticks, - struct timeval *tv) -{ - unsigned long sec, sec_rem, usec; - - usec = ((fast_ticks * highres_timer_usec_prescale) - >> HIGHRES_TIMER_USEC_SHIFT); - - sec = slow_ticks / HIGHRES_TIMER_SLOW_TICK_RATE; - sec_rem = slow_ticks % HIGHRES_TIMER_SLOW_TICK_RATE; - - usec += sec_rem * (1000000 / HIGHRES_TIMER_SLOW_TICK_RATE); - - tv->tv_sec = sec; - tv->tv_usec = usec; -} - -void highres_timer_read (struct timeval *tv) -{ - u32 fast_ticks, slow_ticks; - highres_timer_read_ticks (&slow_ticks, &fast_ticks); - highres_timer_ticks_to_timeval (slow_ticks, fast_ticks, tv); -} diff --git a/arch/v850/kernel/init_task.c b/arch/v850/kernel/init_task.c deleted file mode 100644 index 44b274dff33f..000000000000 --- a/arch/v850/kernel/init_task.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * arch/v850/kernel/init_task.c -- Initial task/thread structures - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * 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 for more details. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static struct fs_struct init_fs = INIT_FS; -static struct signal_struct init_signals = INIT_SIGNALS (init_signals); -static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); -struct mm_struct init_mm = INIT_MM (init_mm); - -EXPORT_SYMBOL(init_mm); - -/* - * Initial task structure. - * - * All other task structs will be allocated on slabs in fork.c - */ -struct task_struct init_task = INIT_TASK (init_task); - -EXPORT_SYMBOL(init_task); - -/* - * Initial thread structure. - * - * We need to make sure that this is 8192-byte aligned due to the - * way process stacks are handled. This is done by having a special - * "init_task" linker map entry. - */ -union thread_union init_thread_union - __attribute__((__section__(".data.init_task"))) = - { INIT_THREAD_INFO(init_task) }; diff --git a/arch/v850/kernel/intv.S b/arch/v850/kernel/intv.S deleted file mode 100644 index 671e4c6150dd..000000000000 --- a/arch/v850/kernel/intv.S +++ /dev/null @@ -1,87 +0,0 @@ -/* - * arch/v850/kernel/intv.S -- Interrupt vectors - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include - -#ifdef CONFIG_V850E_HIGHRES_TIMER -#include -#endif - -/* Jump to an interrupt/trap handler. These handlers (defined in entry.S) - expect the stack-pointer to be saved in ENTRY_SP, so we use sp to do an - indirect jump (which avoids problems when the handler is more than a signed - 22-bit offset away). */ -#define JUMP_TO_HANDLER(name, sp_save_loc) \ - st.w sp, sp_save_loc; \ - mov hilo(name), sp; \ - jmp [sp] - - - /* Reset vector. */ - .section .intv.reset, "ax" - .org 0x0 - mov hilo(C_SYMBOL_NAME(start)), r1; - jmp [r1] - - - /* Generic interrupt vectors. */ - .section .intv.common, "ax" - .balign 0x10 - JUMP_TO_HANDLER (nmi, NMI_ENTRY_SP) // 0x10 - NMI0 - .balign 0x10 - JUMP_TO_HANDLER (nmi, NMI_ENTRY_SP) // 0x20 - NMI1 - .balign 0x10 - JUMP_TO_HANDLER (nmi, NMI_ENTRY_SP) // 0x30 - NMI2 - - .balign 0x10 - JUMP_TO_HANDLER (trap, ENTRY_SP) // 0x40 - TRAP0n - .balign 0x10 - JUMP_TO_HANDLER (trap, ENTRY_SP) // 0x50 - TRAP1n - - .balign 0x10 - JUMP_TO_HANDLER (dbtrap, ENTRY_SP) // 0x60 - Illegal op / DBTRAP insn - - - /* Hardware interrupt vectors. */ - .section .intv.mach, "ax" - .org 0x0 - -#if defined (CONFIG_V850E_HIGHRES_TIMER) && defined (IRQ_INTCMD) - - /* Interrupts before the highres timer interrupt. */ - .rept IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT) - .balign 0x10 - JUMP_TO_HANDLER (irq, ENTRY_SP) - .endr - - /* The highres timer interrupt. */ - .balign 0x10 - JUMP_TO_HANDLER (C_SYMBOL_NAME (highres_timer_slow_tick_irq), ENTRY_SP) - - /* Interrupts after the highres timer interrupt. */ - .rept NUM_CPU_IRQS - IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT) - 1 - .balign 0x10 - JUMP_TO_HANDLER (irq, ENTRY_SP) - .endr - -#else /* No highres timer */ - - .rept NUM_CPU_IRQS - .balign 0x10 - JUMP_TO_HANDLER (irq, ENTRY_SP) - .endr - -#endif /* Highres timer */ diff --git a/arch/v850/kernel/irq.c b/arch/v850/kernel/irq.c deleted file mode 100644 index 858c45819aab..000000000000 --- a/arch/v850/kernel/irq.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * arch/v850/kernel/irq.c -- High-level interrupt handling - * - * Copyright (C) 2001,02,03,04,05 NEC Electronics Corporation - * Copyright (C) 2001,02,03,04,05 Miles Bader - * Copyright (C) 1994-2000 Ralf Baechle - * Copyright (C) 1992 Linus Torvalds - * - * 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 for more details. - * - * This file was was derived from the mips version, arch/mips/kernel/irq.c - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* - * 'what should we do if we get a hw irq event on an illegal vector'. - * each architecture has to answer this themselves, it doesn't deserve - * a generic callback i think. - */ -void ack_bad_irq(unsigned int irq) -{ - printk("received IRQ %d with unknown interrupt type\n", irq); -} - -volatile unsigned long irq_err_count, spurious_count; - -/* - * Generic, controller-independent functions: - */ - -int show_interrupts(struct seq_file *p, void *v) -{ - int irq = *(loff_t *) v; - - if (irq == 0) { - int cpu; - seq_puts(p, " "); - for (cpu=0; cpu < 1 /*smp_num_cpus*/; cpu++) - seq_printf(p, "CPU%d ", cpu); - seq_putc(p, '\n'); - } - - if (irq < NR_IRQS) { - unsigned long flags; - struct irqaction *action; - - spin_lock_irqsave(&irq_desc[irq].lock, flags); - - action = irq_desc[irq].action; - if (action) { - int j; - int count = 0; - int num = -1; - const char *type_name = irq_desc[irq].chip->typename; - - for (j = 0; j < NR_IRQS; j++) - if (irq_desc[j].chip->typename == type_name){ - if (irq == j) - num = count; - count++; - } - - seq_printf(p, "%3d: ",irq); - seq_printf(p, "%10u ", kstat_irqs(irq)); - if (count > 1) { - int prec = (num >= 100 ? 3 : num >= 10 ? 2 : 1); - seq_printf(p, " %*s%d", 14 - prec, - type_name, num); - } else - seq_printf(p, " %14s", type_name); - - seq_printf(p, " %s", action->name); - for (action=action->next; action; action = action->next) - seq_printf(p, ", %s", action->name); - seq_putc(p, '\n'); - } - - spin_unlock_irqrestore(&irq_desc[irq].lock, flags); - } else if (irq == NR_IRQS) - seq_printf(p, "ERR: %10lu\n", irq_err_count); - - return 0; -} - -/* Handle interrupt IRQ. REGS are the registers at the time of ther - interrupt. */ -unsigned int handle_irq (int irq, struct pt_regs *regs) -{ - irq_enter(); - __do_IRQ(irq, regs); - irq_exit(); - return 1; -} - -/* Initialize irq handling for IRQs. - BASE_IRQ, BASE_IRQ+INTERVAL, ..., BASE_IRQ+NUM*INTERVAL - to IRQ_TYPE. An IRQ_TYPE of 0 means to use a generic interrupt type. */ -void __init -init_irq_handlers (int base_irq, int num, int interval, - struct hw_interrupt_type *irq_type) -{ - while (num-- > 0) { - irq_desc[base_irq].status = IRQ_DISABLED; - irq_desc[base_irq].action = NULL; - irq_desc[base_irq].depth = 1; - irq_desc[base_irq].chip = irq_type; - base_irq += interval; - } -} diff --git a/arch/v850/kernel/ma.c b/arch/v850/kernel/ma.c deleted file mode 100644 index 143774de75e1..000000000000 --- a/arch/v850/kernel/ma.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * arch/v850/kernel/ma.c -- V850E/MA series of cpu chips - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mach.h" - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Start hardware timer. */ - v850e_timer_d_configure (0, HZ); - /* Install timer interrupt handler. */ - setup_irq (IRQ_INTCMD(0), timer_action); -} - -static struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_MACH_IRQS, 1, 7 }, - { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 }, - { "DMA", IRQ_INTDMA(0), IRQ_INTDMA_NUM, 1, 2 }, - { "CSI", IRQ_INTCSI(0), IRQ_INTCSI_NUM, 4, 4 }, - { "SER", IRQ_INTSER(0), IRQ_INTSER_NUM, 4, 3 }, - { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 4, 4 }, - { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 4, 5 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -/* Initialize MA chip interrupts. */ -void __init ma_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - -/* Called before configuring an on-chip UART. */ -void ma_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud) -{ - /* We only know about the first two UART channels (though - specific chips may have more). */ - if (chan < 2) { - unsigned bits = 0x3 << (chan * 3); - /* Specify that the relevant pins on the chip should do - serial I/O, not direct I/O. */ - MA_PORT4_PMC |= bits; - /* Specify that we're using the UART, not the CSI device. */ - MA_PORT4_PFC |= bits; - } -} diff --git a/arch/v850/kernel/mach.c b/arch/v850/kernel/mach.c deleted file mode 100644 index b9db278d2b71..000000000000 --- a/arch/v850/kernel/mach.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * arch/v850/kernel/mach.c -- Defaults for some things defined by "mach.h" - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include "mach.h" - -/* Called with each timer tick, if non-zero. */ -void (*mach_tick)(void) = 0; diff --git a/arch/v850/kernel/mach.h b/arch/v850/kernel/mach.h deleted file mode 100644 index 9e0e4816ec56..000000000000 --- a/arch/v850/kernel/mach.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * arch/v850/kernel/mach.h -- Machine-dependent functions used by v850 port - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_MACH_H__ -#define __V850_MACH_H__ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -void mach_setup (char **cmdline); -void mach_gettimeofday (struct timespec *tv); -void mach_sched_init (struct irqaction *timer_action); -void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len); -void mach_init_irqs (void); - -/* If defined, is called very early in the kernel initialization. The - stack pointer is valid, but very little has been initialized (e.g., - bss is not zeroed yet) when this is called, so care must taken. */ -void mach_early_init (void); - -/* If defined, called after the bootmem allocator has been initialized, - to allow the platform-dependent code to reserve any areas of RAM that - the kernel shouldn't touch. */ -void mach_reserve_bootmem (void) __attribute__ ((__weak__)); - -/* Called with each timer tick, if non-zero. */ -extern void (*mach_tick) (void); - -/* The following establishes aliases for various mach_ functions to the - name by which the rest of the kernel calls them. These statements - should only have an effect in the file that defines the actual functions. */ -#define MACH_ALIAS(to, from) \ - asm (".global " macrology_stringify (C_SYMBOL_NAME (to)) ";" \ - macrology_stringify (C_SYMBOL_NAME (to)) \ - " = " macrology_stringify (C_SYMBOL_NAME (from))) -/* e.g.: MACH_ALIAS (kernel_name, arch_spec_name); */ - -#endif /* __V850_MACH_H__ */ diff --git a/arch/v850/kernel/me2.c b/arch/v850/kernel/me2.c deleted file mode 100644 index 007115dc9ce0..000000000000 --- a/arch/v850/kernel/me2.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * arch/v850/kernel/me2.c -- V850E/ME2 chip-specific support - * - * Copyright (C) 2003 NEC Corporation - * Copyright (C) 2003 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mach.h" - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Start hardware timer. */ - v850e_timer_d_configure (0, HZ); - /* Install timer interrupt handler. */ - setup_irq (IRQ_INTCMD(0), timer_action); -} - -static struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_CPU_IRQS, 1, 7 }, - { "INTP", IRQ_INTP(0), IRQ_INTP_NUM, 1, 5 }, - { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 3 }, - { "UBTIRE", IRQ_INTUBTIRE(0), IRQ_INTUBTIRE_NUM, 5, 4 }, - { "UBTIR", IRQ_INTUBTIR(0), IRQ_INTUBTIR_NUM, 5, 4 }, - { "UBTIT", IRQ_INTUBTIT(0), IRQ_INTUBTIT_NUM, 5, 4 }, - { "UBTIF", IRQ_INTUBTIF(0), IRQ_INTUBTIF_NUM, 5, 4 }, - { "UBTITO", IRQ_INTUBTITO(0), IRQ_INTUBTITO_NUM, 5, 4 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -/* Initialize V850E/ME2 chip interrupts. */ -void __init me2_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - -/* Called before configuring an on-chip UART. */ -void me2_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud) -{ - if (chan == 0) { - /* Specify that the relevant pins on the chip should do - serial I/O, not direct I/O. */ - ME2_PORT1_PMC |= 0xC; - /* Specify that we're using the UART, not the CSI device. */ - ME2_PORT1_PFC |= 0xC; - } else if (chan == 1) { - /* Specify that the relevant pins on the chip should do - serial I/O, not direct I/O. */ - ME2_PORT2_PMC |= 0x6; - /* Specify that we're using the UART, not the CSI device. */ - ME2_PORT2_PFC |= 0x6; - } -} diff --git a/arch/v850/kernel/memcons.c b/arch/v850/kernel/memcons.c deleted file mode 100644 index 92f514fdcc79..000000000000 --- a/arch/v850/kernel/memcons.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * arch/v850/kernel/memcons.c -- Console I/O to a memory buffer - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include - -/* If this device is enabled, the linker map should define start and - end points for its buffer. */ -extern char memcons_output[], memcons_output_end; - -/* Current offset into the buffer. */ -static unsigned long memcons_offs = 0; - -/* Spinlock protecting memcons_offs. */ -static DEFINE_SPINLOCK(memcons_lock); - - -static size_t write (const char *buf, size_t len) -{ - unsigned long flags; - char *point; - - spin_lock_irqsave (memcons_lock, flags); - - point = memcons_output + memcons_offs; - if (point + len >= &memcons_output_end) { - len = &memcons_output_end - point; - memcons_offs = 0; - } else - memcons_offs += len; - - spin_unlock_irqrestore (memcons_lock, flags); - - memcpy (point, buf, len); - - return len; -} - - -/* Low-level console. */ - -static void memcons_write (struct console *co, const char *buf, unsigned len) -{ - while (len > 0) - len -= write (buf, len); -} - -static struct tty_driver *tty_driver; - -static struct tty_driver *memcons_device (struct console *co, int *index) -{ - *index = co->index; - return tty_driver; -} - -static struct console memcons = -{ - .name = "memcons", - .write = memcons_write, - .device = memcons_device, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -void memcons_setup (void) -{ - register_console (&memcons); - printk (KERN_INFO "Console: static memory buffer (memcons)\n"); -} - -/* Higher level TTY interface. */ - -int memcons_tty_open (struct tty_struct *tty, struct file *filp) -{ - return 0; -} - -int memcons_tty_write (struct tty_struct *tty, const unsigned char *buf, int len) -{ - return write (buf, len); -} - -int memcons_tty_write_room (struct tty_struct *tty) -{ - return &memcons_output_end - (memcons_output + memcons_offs); -} - -int memcons_tty_chars_in_buffer (struct tty_struct *tty) -{ - /* We have no buffer. */ - return 0; -} - -static const struct tty_operations ops = { - .open = memcons_tty_open, - .write = memcons_tty_write, - .write_room = memcons_tty_write_room, - .chars_in_buffer = memcons_tty_chars_in_buffer, -}; - -int __init memcons_tty_init (void) -{ - int err; - struct tty_driver *driver = alloc_tty_driver(1); - if (!driver) - return -ENOMEM; - - driver->name = "memcons"; - driver->major = TTY_MAJOR; - driver->minor_start = 64; - driver->type = TTY_DRIVER_TYPE_SYSCONS; - driver->init_termios = tty_std_termios; - tty_set_operations(driver, &ops); - err = tty_register_driver(driver); - if (err) { - put_tty_driver(driver); - return err; - } - tty_driver = driver; - return 0; -} -__initcall (memcons_tty_init); diff --git a/arch/v850/kernel/module.c b/arch/v850/kernel/module.c deleted file mode 100644 index 64aeb3e37c52..000000000000 --- a/arch/v850/kernel/module.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * arch/v850/kernel/module.c -- Architecture-specific module functions - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * Copyright (C) 2001,03 Rusty Russell - * - * 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 for more details. - * - * Written by Miles Bader - * - * Derived in part from arch/ppc/kernel/module.c - */ - -#include -#include -#include -#include - -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(fmt , ...) -#endif - -void *module_alloc (unsigned long size) -{ - return size == 0 ? 0 : vmalloc (size); -} - -void module_free (struct module *mod, void *module_region) -{ - vfree (module_region); - /* FIXME: If module_region == mod->init_region, trim exception - table entries. */ -} - -int module_finalize (const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, - struct module *mod) -{ - return 0; -} - -/* Count how many different relocations (different symbol, different - addend) */ -static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) -{ - unsigned int i, j, ret = 0; - - /* Sure, this is order(n^2), but it's usually short, and not - time critical */ - for (i = 0; i < num; i++) { - for (j = 0; j < i; j++) { - /* If this addend appeared before, it's - already been counted */ - if (ELF32_R_SYM(rela[i].r_info) - == ELF32_R_SYM(rela[j].r_info) - && rela[i].r_addend == rela[j].r_addend) - break; - } - if (j == i) ret++; - } - return ret; -} - -/* Get the potential trampolines size required of the init and - non-init sections */ -static unsigned long get_plt_size(const Elf32_Ehdr *hdr, - const Elf32_Shdr *sechdrs, - const char *secstrings, - int is_init) -{ - unsigned long ret = 0; - unsigned i; - - /* Everything marked ALLOC (this includes the exported - symbols) */ - for (i = 1; i < hdr->e_shnum; i++) { - /* If it's called *.init*, and we're not init, we're - not interested */ - if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) - != is_init) - continue; - - if (sechdrs[i].sh_type == SHT_RELA) { - DEBUGP("Found relocations in section %u\n", i); - DEBUGP("Ptr: %p. Number: %u\n", - (void *)hdr + sechdrs[i].sh_offset, - sechdrs[i].sh_size / sizeof(Elf32_Rela)); - ret += count_relocs((void *)hdr - + sechdrs[i].sh_offset, - sechdrs[i].sh_size - / sizeof(Elf32_Rela)) - * sizeof(struct v850_plt_entry); - } - } - - return ret; -} - -int module_frob_arch_sections(Elf32_Ehdr *hdr, - Elf32_Shdr *sechdrs, - char *secstrings, - struct module *me) -{ - unsigned int i; - - /* Find .plt and .pltinit sections */ - for (i = 0; i < hdr->e_shnum; i++) { - if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0) - me->arch.init_plt_section = i; - else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0) - me->arch.core_plt_section = i; - } - if (!me->arch.core_plt_section || !me->arch.init_plt_section) { - printk("Module doesn't contain .plt or .plt.init sections.\n"); - return -ENOEXEC; - } - - /* Override their sizes */ - sechdrs[me->arch.core_plt_section].sh_size - = get_plt_size(hdr, sechdrs, secstrings, 0); - sechdrs[me->arch.init_plt_section].sh_size - = get_plt_size(hdr, sechdrs, secstrings, 1); - return 0; -} - -int apply_relocate (Elf32_Shdr *sechdrs, const char *strtab, - unsigned int symindex, unsigned int relsec, - struct module *mod) -{ - printk ("Barf\n"); - return -ENOEXEC; -} - -/* Set up a trampoline in the PLT to bounce us to the distant function */ -static uint32_t do_plt_call (void *location, Elf32_Addr val, - Elf32_Shdr *sechdrs, struct module *mod) -{ - struct v850_plt_entry *entry; - /* Instructions used to do the indirect jump. */ - uint32_t tramp[2]; - - /* We have to trash a register, so we assume that any control - transfer more than 21-bits away must be a function call - (so we can use a call-clobbered register). */ - tramp[0] = 0x0621 + ((val & 0xffff) << 16); /* mov sym, r1 ... */ - tramp[1] = ((val >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */ - - /* Init, or core PLT? */ - if (location >= mod->module_core - && location < mod->module_core + mod->core_size) - entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr; - else - entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr; - - /* Find this entry, or if that fails, the next avail. entry */ - while (entry->tramp[0]) - if (entry->tramp[0] == tramp[0] && entry->tramp[1] == tramp[1]) - return (uint32_t)entry; - else - entry++; - - entry->tramp[0] = tramp[0]; - entry->tramp[1] = tramp[1]; - - return (uint32_t)entry; -} - -int apply_relocate_add (Elf32_Shdr *sechdrs, const char *strtab, - unsigned int symindex, unsigned int relsec, - struct module *mod) -{ - unsigned int i; - Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; - - DEBUGP ("Applying relocate section %u to %u\n", relsec, - sechdrs[relsec].sh_info); - - for (i = 0; i < sechdrs[relsec].sh_size / sizeof (*rela); i++) { - /* This is where to make the change */ - uint32_t *loc - = ((void *)sechdrs[sechdrs[relsec].sh_info].sh_addr - + rela[i].r_offset); - /* This is the symbol it is referring to. Note that all - undefined symbols have been resolved. */ - Elf32_Sym *sym - = ((Elf32_Sym *)sechdrs[symindex].sh_addr - + ELF32_R_SYM (rela[i].r_info)); - uint32_t val = sym->st_value + rela[i].r_addend; - - switch (ELF32_R_TYPE (rela[i].r_info)) { - case R_V850_32: - /* We write two shorts instead of a long because even - 32-bit insns only need half-word alignment, but - 32-bit data writes need to be long-word aligned. */ - val += ((uint16_t *)loc)[0]; - val += ((uint16_t *)loc)[1] << 16; - ((uint16_t *)loc)[0] = val & 0xffff; - ((uint16_t *)loc)[1] = (val >> 16) & 0xffff; - break; - - case R_V850_22_PCREL: - /* Maybe jump indirectly via a PLT table entry. */ - if ((int32_t)(val - (uint32_t)loc) > 0x1fffff - || (int32_t)(val - (uint32_t)loc) < -0x200000) - val = do_plt_call (loc, val, sechdrs, mod); - - val -= (uint32_t)loc; - - /* We write two shorts instead of a long because - even 32-bit insns only need half-word alignment, - but 32-bit data writes need to be long-word - aligned. */ - ((uint16_t *)loc)[0] = - (*(uint16_t *)loc & 0xffc0) /* opcode + reg */ - | ((val >> 16) & 0xffc03f); /* offs high */ - ((uint16_t *)loc)[1] = - (val & 0xffff); /* offs low */ - break; - - default: - printk (KERN_ERR "module %s: Unknown reloc: %u\n", - mod->name, ELF32_R_TYPE (rela[i].r_info)); - return -ENOEXEC; - } - } - - return 0; -} - -void -module_arch_cleanup(struct module *mod) -{ -} diff --git a/arch/v850/kernel/process.c b/arch/v850/kernel/process.c deleted file mode 100644 index e4a4b8e7d5a3..000000000000 --- a/arch/v850/kernel/process.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * arch/v850/kernel/process.c -- Arch-dependent process handling - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -void (*pm_power_off)(void) = NULL; -EXPORT_SYMBOL(pm_power_off); - -extern void ret_from_fork (void); - - -/* The idle loop. */ -static void default_idle (void) -{ - while (! need_resched ()) - asm ("halt; nop; nop; nop; nop; nop" ::: "cc"); -} - -void (*idle)(void) = default_idle; - -/* - * The idle thread. There's no useful work to be - * done, so just try to conserve power and have a - * low exit latency (ie sit in a loop waiting for - * somebody to say that they'd like to reschedule) - */ -void cpu_idle (void) -{ - /* endless idle loop with no priority at all */ - while (1) { - while (!need_resched()) - (*idle) (); - - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - } -} - -/* - * This is the mechanism for creating a new kernel thread. - * - * NOTE! Only a kernel-only process (ie the swapper or direct descendants who - * haven't done an "execve()") should use this: it will work within a system - * call from a "real" process, but the process memory space will not be free'd - * until both the parent and the child have exited. - */ -int kernel_thread (int (*fn)(void *), void *arg, unsigned long flags) -{ - register mm_segment_t fs = get_fs (); - register unsigned long syscall asm (SYSCALL_NUM); - register unsigned long arg0 asm (SYSCALL_ARG0); - register unsigned long ret asm (SYSCALL_RET); - - set_fs (KERNEL_DS); - - /* Clone this thread. Note that we don't pass the clone syscall's - second argument -- it's ignored for calls from kernel mode (the - child's SP is always set to the top of the kernel stack). */ - arg0 = flags | CLONE_VM; - syscall = __NR_clone; - asm volatile ("trap " SYSCALL_SHORT_TRAP - : "=r" (ret), "=r" (syscall) - : "1" (syscall), "r" (arg0) - : SYSCALL_SHORT_CLOBBERS); - - if (ret == 0) { - /* In child thread, call FN and exit. */ - arg0 = (*fn) (arg); - syscall = __NR_exit; - asm volatile ("trap " SYSCALL_SHORT_TRAP - : "=r" (ret), "=r" (syscall) - : "1" (syscall), "r" (arg0) - : SYSCALL_SHORT_CLOBBERS); - } - - /* In parent. */ - set_fs (fs); - - return ret; -} - -void flush_thread (void) -{ - set_fs (USER_DS); -} - -int copy_thread (int nr, unsigned long clone_flags, - unsigned long stack_start, unsigned long stack_size, - struct task_struct *p, struct pt_regs *regs) -{ - /* Start pushing stuff from the top of the child's kernel stack. */ - unsigned long orig_ksp = task_tos(p); - unsigned long ksp = orig_ksp; - /* We push two `state save' stack fames (see entry.S) on the new - kernel stack: - 1) The innermost one is what switch_thread would have - pushed, and is used when we context switch to the child - thread for the first time. It's set up to return to - ret_from_fork in entry.S. - 2) The outermost one (nearest the top) is what a syscall - trap would have pushed, and is set up to return to the - same location as the parent thread, but with a return - value of 0. */ - struct pt_regs *child_switch_regs, *child_trap_regs; - - /* Trap frame. */ - ksp -= STATE_SAVE_SIZE; - child_trap_regs = (struct pt_regs *)(ksp + STATE_SAVE_PT_OFFSET); - /* Switch frame. */ - ksp -= STATE_SAVE_SIZE; - child_switch_regs = (struct pt_regs *)(ksp + STATE_SAVE_PT_OFFSET); - - /* First copy parent's register state to child. */ - *child_switch_regs = *regs; - *child_trap_regs = *regs; - - /* switch_thread returns to the restored value of the lp - register (r31), so we make that the place where we want to - jump when the child thread begins running. */ - child_switch_regs->gpr[GPR_LP] = (v850_reg_t)ret_from_fork; - - if (regs->kernel_mode) - /* Since we're returning to kernel-mode, make sure the child's - stored kernel stack pointer agrees with what the actual - stack pointer will be at that point (the trap return code - always restores the SP, even when returning to - kernel-mode). */ - child_trap_regs->gpr[GPR_SP] = orig_ksp; - else - /* Set the child's user-mode stack-pointer (the name - `stack_start' is a misnomer, it's just the initial SP - value). */ - child_trap_regs->gpr[GPR_SP] = stack_start; - - /* Thread state for the child (everything else is on the stack). */ - p->thread.ksp = ksp; - - return 0; -} - -/* - * sys_execve() executes a new program. - */ -int sys_execve (char *name, char **argv, char **envp, struct pt_regs *regs) -{ - char *filename = getname (name); - int error = PTR_ERR (filename); - - if (! IS_ERR (filename)) { - error = do_execve (filename, argv, envp, regs); - putname (filename); - } - - return error; -} - - -/* - * These bracket the sleeping functions.. - */ -#define first_sched ((unsigned long)__sched_text_start) -#define last_sched ((unsigned long)__sched_text_end) - -unsigned long get_wchan (struct task_struct *p) -{ -#if 0 /* Barf. Figure out the stack-layout later. XXX */ - unsigned long fp, pc; - int count = 0; - - if (!p || p == current || p->state == TASK_RUNNING) - return 0; - - pc = thread_saved_pc (p); - - /* This quite disgusting function walks up the stack, following - saved return address, until it something that's out of bounds - (as defined by `first_sched' and `last_sched'). It then - returns the last PC that was in-bounds. */ - do { - if (fp < stack_page + sizeof (struct task_struct) || - fp >= 8184+stack_page) - return 0; - pc = ((unsigned long *)fp)[1]; - if (pc < first_sched || pc >= last_sched) - return pc; - fp = *(unsigned long *) fp; - } while (count++ < 16); -#endif - - return 0; -} diff --git a/arch/v850/kernel/procfs.c b/arch/v850/kernel/procfs.c deleted file mode 100644 index e433cde789b4..000000000000 --- a/arch/v850/kernel/procfs.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * arch/v850/kernel/procfs.c -- Introspection functions for /proc filesystem - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include "mach.h" - -static int cpuinfo_print (struct seq_file *m, void *v) -{ - extern unsigned long loops_per_jiffy; - - seq_printf (m, "CPU-Family: v850\nCPU-Arch: %s\n", CPU_ARCH); - -#ifdef CPU_MODEL_LONG - seq_printf (m, "CPU-Model: %s (%s)\n", CPU_MODEL, CPU_MODEL_LONG); -#else - seq_printf (m, "CPU-Model: %s\n", CPU_MODEL); -#endif - -#ifdef CPU_CLOCK_FREQ - seq_printf (m, "CPU-Clock: %ld (%ld MHz)\n", - (long)CPU_CLOCK_FREQ, - (long)CPU_CLOCK_FREQ / 1000000); -#endif - - seq_printf (m, "BogoMips: %lu.%02lu\n", - loops_per_jiffy/(500000/HZ), - (loops_per_jiffy/(5000/HZ)) % 100); - -#ifdef PLATFORM_LONG - seq_printf (m, "Platform: %s (%s)\n", PLATFORM, PLATFORM_LONG); -#elif defined (PLATFORM) - seq_printf (m, "Platform: %s\n", PLATFORM); -#endif - - return 0; -} - -static void *cpuinfo_start (struct seq_file *m, loff_t *pos) -{ - return *pos < NR_CPUS ? ((void *) 0x12345678) : NULL; -} - -static void *cpuinfo_next (struct seq_file *m, void *v, loff_t *pos) -{ - ++*pos; - return cpuinfo_start (m, pos); -} - -static void cpuinfo_stop (struct seq_file *m, void *v) -{ -} - -const struct seq_operations cpuinfo_op = { - .start = cpuinfo_start, - .next = cpuinfo_next, - .stop = cpuinfo_stop, - .show = cpuinfo_print -}; diff --git a/arch/v850/kernel/ptrace.c b/arch/v850/kernel/ptrace.c deleted file mode 100644 index a458ac941b25..000000000000 --- a/arch/v850/kernel/ptrace.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * arch/v850/kernel/ptrace.c -- `ptrace' system call - * - * Copyright (C) 2002,03,04 NEC Electronics Corporation - * Copyright (C) 2002,03,04 Miles Bader - * - * Derived from arch/mips/kernel/ptrace.c: - * - * Copyright (C) 1992 Ross Biro - * Copyright (C) Linus Torvalds - * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle - * Copyright (C) 1996 David S. Miller - * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com - * Copyright (C) 1999 MIPS Technologies, Inc. - * - * 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 for more details. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* Returns the address where the register at REG_OFFS in P is stashed away. */ -static v850_reg_t *reg_save_addr (unsigned reg_offs, struct task_struct *t) -{ - struct pt_regs *regs; - - /* Three basic cases: - - (1) A register normally saved before calling the scheduler, is - available in the kernel entry pt_regs structure at the top - of the kernel stack. The kernel trap/irq exit path takes - care to save/restore almost all registers for ptrace'd - processes. - - (2) A call-clobbered register, where the process P entered the - kernel via [syscall] trap, is not stored anywhere; that's - OK, because such registers are not expected to be preserved - when the trap returns anyway (so we don't actually bother to - test for this case). - - (3) A few registers not used at all by the kernel, and so - normally never saved except by context-switches, are in the - context switch state. */ - - if (reg_offs == PT_CTPC || reg_offs == PT_CTPSW || reg_offs == PT_CTBP) - /* Register saved during context switch. */ - regs = thread_saved_regs (t); - else - /* Register saved during kernel entry (or not available). */ - regs = task_pt_regs (t); - - return (v850_reg_t *)((char *)regs + reg_offs); -} - -/* Set the bits SET and clear the bits CLEAR in the v850e DIR - (`debug information register'). Returns the new value of DIR. */ -static inline v850_reg_t set_dir (v850_reg_t set, v850_reg_t clear) -{ - register v850_reg_t rval asm ("r10"); - register v850_reg_t arg0 asm ("r6") = set; - register v850_reg_t arg1 asm ("r7") = clear; - - /* The dbtrap handler has exactly this functionality when called - from kernel mode. 0xf840 is a `dbtrap' insn. */ - asm (".short 0xf840" : "=r" (rval) : "r" (arg0), "r" (arg1)); - - return rval; -} - -/* Makes sure hardware single-stepping is (globally) enabled. - Returns true if successful. */ -static inline int enable_single_stepping (void) -{ - static int enabled = 0; /* Remember whether we already did it. */ - if (! enabled) { - /* Turn on the SE (`single-step enable') bit, 0x100, in the - DIR (`debug information register'). This may fail if a - processor doesn't support it or something. We also try - to clear bit 0x40 (`INI'), which is necessary to use the - debug stuff on the v850e2; on the v850e, clearing 0x40 - shouldn't cause any problem. */ - v850_reg_t dir = set_dir (0x100, 0x40); - /* Make sure it really got set. */ - if (dir & 0x100) - enabled = 1; - } - return enabled; -} - -/* Try to set CHILD's single-step flag to VAL. Returns true if successful. */ -static int set_single_step (struct task_struct *t, int val) -{ - v850_reg_t *psw_addr = reg_save_addr(PT_PSW, t); - if (val) { - /* Make sure single-stepping is enabled. */ - if (! enable_single_stepping ()) - return 0; - /* Set T's single-step flag. */ - *psw_addr |= 0x800; - } else - *psw_addr &= ~0x800; - return 1; -} - -long arch_ptrace(struct task_struct *child, long request, long addr, long data) -{ - int rval; - - switch (request) { - unsigned long val; - - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: - rval = generic_ptrace_peekdata(child, addr, data); - goto out; - - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - rval = generic_ptrace_pokedata(child, addr, data); - goto out; - - /* Read/write the word at location ADDR in the registers. */ - case PTRACE_PEEKUSR: - case PTRACE_POKEUSR: - rval = 0; - if (addr >= PT_SIZE && request == PTRACE_PEEKUSR) { - /* Special requests that don't actually correspond - to offsets in struct pt_regs. */ - if (addr == PT_TEXT_ADDR) - val = child->mm->start_code; - else if (addr == PT_DATA_ADDR) - val = child->mm->start_data; - else if (addr == PT_TEXT_LEN) - val = child->mm->end_code - - child->mm->start_code; - else - rval = -EIO; - } else if (addr >= 0 && addr < PT_SIZE && (addr & 0x3) == 0) { - v850_reg_t *reg_addr = reg_save_addr(addr, child); - if (request == PTRACE_PEEKUSR) - val = *reg_addr; - else - *reg_addr = data; - } else - rval = -EIO; - - if (rval == 0 && request == PTRACE_PEEKUSR) - rval = put_user (val, (unsigned long *)data); - goto out; - - /* Continue and stop at next (return from) syscall */ - case PTRACE_SYSCALL: - /* Restart after a signal. */ - case PTRACE_CONT: - /* Execute a single instruction. */ - case PTRACE_SINGLESTEP: - rval = -EIO; - if (!valid_signal(data)) - break; - - /* Turn CHILD's single-step flag on or off. */ - if (! set_single_step (child, request == PTRACE_SINGLESTEP)) - break; - - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - - child->exit_code = data; - wake_up_process(child); - rval = 0; - break; - - /* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. - */ - case PTRACE_KILL: - rval = 0; - if (child->exit_state == EXIT_ZOMBIE) /* already dead */ - break; - child->exit_code = SIGKILL; - wake_up_process(child); - break; - - case PTRACE_DETACH: /* detach a process that was attached. */ - set_single_step (child, 0); /* Clear single-step flag */ - rval = ptrace_detach(child, data); - break; - - default: - rval = -EIO; - goto out; - } - out: - return rval; -} - -asmlinkage void syscall_trace(void) -{ - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - return; - if (!(current->ptrace & PT_PTRACED)) - return; - /* The 0x80 provides a way for the tracing parent to distinguish - between a syscall stop and SIGTRAP delivery */ - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0)); - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; - } -} - -void ptrace_disable (struct task_struct *child) -{ - /* nothing to do */ -} diff --git a/arch/v850/kernel/rte_cb.c b/arch/v850/kernel/rte_cb.c deleted file mode 100644 index 43018e1edebd..000000000000 --- a/arch/v850/kernel/rte_cb.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * include/asm-v850/rte_cb.c -- Midas lab RTE-CB series of evaluation boards - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include "mach.h" - -static void led_tick (void); - -/* LED access routines. */ -extern unsigned read_leds (int pos, char *buf, int len); -extern unsigned write_leds (int pos, const char *buf, int len); - -#ifdef CONFIG_RTE_CB_MULTI -extern void multi_init (void); -#endif - - -void __init rte_cb_early_init (void) -{ - v850e_intc_disable_irqs (); - -#ifdef CONFIG_RTE_CB_MULTI - multi_init (); -#endif -} - -void __init mach_setup (char **cmdline) -{ -#ifdef CONFIG_RTE_MB_A_PCI - /* Probe for Mother-A, and print a message if we find it. */ - *(volatile unsigned long *)MB_A_SRAM_ADDR = 0xDEADBEEF; - if (*(volatile unsigned long *)MB_A_SRAM_ADDR == 0xDEADBEEF) { - *(volatile unsigned long *)MB_A_SRAM_ADDR = 0x12345678; - if (*(volatile unsigned long *)MB_A_SRAM_ADDR == 0x12345678) - printk (KERN_INFO - " NEC SolutionGear/Midas lab" - " RTE-MOTHER-A motherboard\n"); - } -#endif /* CONFIG_RTE_MB_A_PCI */ - - mach_tick = led_tick; -} - -void machine_restart (char *__unused) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - asm ("jmp r0"); /* Jump to the reset vector. */ -} - -/* This says `HALt.' in LEDese. */ -static unsigned char halt_leds_msg[] = { 0x76, 0x77, 0x38, 0xF8 }; - -void machine_halt (void) -{ -#ifdef CONFIG_RESET_GUARD - disable_reset_guard (); -#endif - - /* Ignore all interrupts. */ - local_irq_disable (); - - /* Write a little message. */ - write_leds (0, halt_leds_msg, sizeof halt_leds_msg); - - /* Really halt. */ - for (;;) - asm ("halt; nop; nop; nop; nop; nop"); -} - -void machine_power_off (void) -{ - machine_halt (); -} - - -/* Animated LED display for timer tick. */ - -#define TICK_UPD_FREQ 6 -static int tick_frames[][10] = { - { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, -1 }, - { 0x63, 0x5c, -1 }, - { 0x5c, 0x00, -1 }, - { 0x63, 0x00, -1 }, - { -1 } -}; - -static void led_tick () -{ - static unsigned counter = 0; - - if (++counter == (HZ / TICK_UPD_FREQ)) { - /* Which frame we're currently displaying for each digit. */ - static unsigned frame_nums[LED_NUM_DIGITS] = { 0 }; - /* Display image. */ - static unsigned char image[LED_NUM_DIGITS] = { 0 }; - unsigned char prev_image[LED_NUM_DIGITS]; - int write_to_leds = 1; /* true if we should actually display */ - int digit; - - /* We check to see if the physical LEDs contains what we last - wrote to them; if not, we suppress display (this is so that - users can write to the LEDs, and not have their output - overwritten). As a special case, we start writing again if - all the LEDs are blank, or our display image is all zeros - (indicating that this is the initial update, when the actual - LEDs might contain random data). */ - read_leds (0, prev_image, LED_NUM_DIGITS); - for (digit = 0; digit < LED_NUM_DIGITS; digit++) - if (image[digit] != prev_image[digit] - && image[digit] && prev_image[digit]) - { - write_to_leds = 0; - break; - } - - /* Update display image. */ - for (digit = 0; - digit < LED_NUM_DIGITS && tick_frames[digit][0] >= 0; - digit++) - { - int frame = tick_frames[digit][frame_nums[digit]]; - if (frame < 0) { - image[digit] = tick_frames[digit][0]; - frame_nums[digit] = 1; - } else { - image[digit] = frame; - frame_nums[digit]++; - break; - } - } - - if (write_to_leds) - /* Write the display image to the physical LEDs. */ - write_leds (0, image, LED_NUM_DIGITS); - - counter = 0; - } -} - - -/* Mother-A interrupts. */ - -#ifdef CONFIG_RTE_GBUS_INT - -#define L GBUS_INT_PRIORITY_LOW -#define M GBUS_INT_PRIORITY_MEDIUM -#define H GBUS_INT_PRIORITY_HIGH - -static struct gbus_int_irq_init gbus_irq_inits[] = { -#ifdef CONFIG_RTE_MB_A_PCI - { "MB_A_LAN", IRQ_MB_A_LAN, 1, 1, L }, - { "MB_A_PCI1", IRQ_MB_A_PCI1(0), IRQ_MB_A_PCI1_NUM, 1, L }, - { "MB_A_PCI2", IRQ_MB_A_PCI2(0), IRQ_MB_A_PCI2_NUM, 1, L }, - { "MB_A_EXT", IRQ_MB_A_EXT(0), IRQ_MB_A_EXT_NUM, 1, L }, - { "MB_A_USB_OC",IRQ_MB_A_USB_OC(0), IRQ_MB_A_USB_OC_NUM, 1, L }, - { "MB_A_PCMCIA_OC",IRQ_MB_A_PCMCIA_OC, 1, 1, L }, -#endif - { 0 } -}; -#define NUM_GBUS_IRQ_INITS (ARRAY_SIZE(gbus_irq_inits) - 1) - -static struct hw_interrupt_type gbus_hw_itypes[NUM_GBUS_IRQ_INITS]; - -#endif /* CONFIG_RTE_GBUS_INT */ - - -void __init rte_cb_init_irqs (void) -{ -#ifdef CONFIG_RTE_GBUS_INT - gbus_int_init_irqs (); - gbus_int_init_irq_types (gbus_irq_inits, gbus_hw_itypes); -#endif /* CONFIG_RTE_GBUS_INT */ -} diff --git a/arch/v850/kernel/rte_cb_leds.c b/arch/v850/kernel/rte_cb_leds.c deleted file mode 100644 index aa47ab1dcd87..000000000000 --- a/arch/v850/kernel/rte_cb_leds.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * include/asm-v850/rte_cb_leds.c -- Midas lab RTE-CB board LED device support - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include - -#include - -#define LEDS_MINOR 169 /* Minor device number, using misc major. */ - -/* The actual LED hardware is write-only, so we hold the contents here too. */ -static unsigned char leds_image[LED_NUM_DIGITS] = { 0 }; - -/* Spinlock protecting the above leds. */ -static DEFINE_SPINLOCK(leds_lock); - -/* Common body of LED read/write functions, checks POS and LEN for - correctness, declares a variable using IMG_DECL, initialized pointing at - the POS position in the LED image buffer, and and iterates COPY_EXPR - until BUF is equal to the last buffer position; finally, sets LEN to be - the amount actually copied. IMG should be a variable declaration - (without an initializer or a terminating semicolon); POS, BUF, and LEN - should all be simple variables. */ -#define DO_LED_COPY(img_decl, pos, buf, len, copy_expr) \ -do { \ - if (pos > LED_NUM_DIGITS) \ - len = 0; \ - else { \ - if (pos + len > LED_NUM_DIGITS) \ - len = LED_NUM_DIGITS - pos; \ - \ - if (len > 0) { \ - unsigned long _flags; \ - const char *_end = buf + len; \ - img_decl = &leds_image[pos]; \ - \ - spin_lock_irqsave (leds_lock, _flags); \ - do \ - (copy_expr); \ - while (buf != _end); \ - spin_unlock_irqrestore (leds_lock, _flags); \ - } \ - } \ -} while (0) - -/* Read LEN bytes from LEDs at position POS, into BUF. - Returns actual amount read. */ -unsigned read_leds (unsigned pos, char *buf, unsigned len) -{ - DO_LED_COPY (const char *img, pos, buf, len, *buf++ = *img++); - return len; -} - -/* Write LEN bytes to LEDs at position POS, from BUF. - Returns actual amount written. */ -unsigned write_leds (unsigned pos, const char *buf, unsigned len) -{ - /* We write the actual LED values backwards, because - increasing memory addresses reflect LEDs right-to-left. */ - volatile char *led = &LED (LED_NUM_DIGITS - pos - 1); - /* We invert the value written to the hardware, because 1 = off, - and 0 = on. */ - DO_LED_COPY (char *img, pos, buf, len, - *led-- = 0xFF ^ (*img++ = *buf++)); - return len; -} - - -/* Device functions. */ - -static ssize_t leds_dev_read (struct file *file, char *buf, size_t len, - loff_t *pos) -{ - char temp_buf[LED_NUM_DIGITS]; - len = read_leds (*pos, temp_buf, len); - if (copy_to_user (buf, temp_buf, len)) - return -EFAULT; - *pos += len; - return len; -} - -static ssize_t leds_dev_write (struct file *file, const char *buf, size_t len, - loff_t *pos) -{ - char temp_buf[LED_NUM_DIGITS]; - if (copy_from_user (temp_buf, buf, min_t(size_t, len, LED_NUM_DIGITS))) - return -EFAULT; - len = write_leds (*pos, temp_buf, len); - *pos += len; - return len; -} - -static loff_t leds_dev_lseek (struct file *file, loff_t offs, int whence) -{ - if (whence == 1) - offs += file->f_pos; /* relative */ - else if (whence == 2) - offs += LED_NUM_DIGITS; /* end-relative */ - - if (offs < 0 || offs > LED_NUM_DIGITS) - return -EINVAL; - - file->f_pos = offs; - - return 0; -} - -static const struct file_operations leds_fops = { - .read = leds_dev_read, - .write = leds_dev_write, - .llseek = leds_dev_lseek -}; - -static struct miscdevice leds_miscdev = { - .name = "leds", - .minor = LEDS_MINOR, - .fops = &leds_fops -}; - -int __init leds_dev_init (void) -{ - return misc_register (&leds_miscdev); -} - -__initcall (leds_dev_init); diff --git a/arch/v850/kernel/rte_cb_multi.c b/arch/v850/kernel/rte_cb_multi.c deleted file mode 100644 index 963d55ab34cc..000000000000 --- a/arch/v850/kernel/rte_cb_multi.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * include/asm-v850/rte_multi.c -- Support for Multi debugger monitor ROM - * on Midas lab RTE-CB series of evaluation boards - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include - -#include - -#define IRQ_ADDR(irq) (0x80 + (irq) * 0x10) - -/* A table of which interrupt vectors to install, since blindly - installing all of them makes the debugger stop working. This is a - list of offsets in the interrupt vector area; each entry means to - copy that particular 16-byte vector. An entry less than zero ends - the table. */ -static long multi_intv_install_table[] = { - /* Trap vectors */ - 0x40, 0x50, - -#ifdef CONFIG_RTE_CB_MULTI_DBTRAP - /* Illegal insn / dbtrap. These are used by multi, so only handle - them if configured to do so. */ - 0x60, -#endif - - /* GINT1 - GINT3 (note, not GINT0!) */ - IRQ_ADDR (IRQ_GINT(1)), - IRQ_ADDR (IRQ_GINT(2)), - IRQ_ADDR (IRQ_GINT(3)), - - /* Timer D interrupts (up to 4 timers) */ - IRQ_ADDR (IRQ_INTCMD(0)), -#if IRQ_INTCMD_NUM > 1 - IRQ_ADDR (IRQ_INTCMD(1)), -#if IRQ_INTCMD_NUM > 2 - IRQ_ADDR (IRQ_INTCMD(2)), -#if IRQ_INTCMD_NUM > 3 - IRQ_ADDR (IRQ_INTCMD(3)), -#endif -#endif -#endif - - /* UART interrupts (up to 3 channels) */ - IRQ_ADDR (IRQ_INTSER (0)), /* err */ - IRQ_ADDR (IRQ_INTSR (0)), /* rx */ - IRQ_ADDR (IRQ_INTST (0)), /* tx */ -#if IRQ_INTSR_NUM > 1 - IRQ_ADDR (IRQ_INTSER (1)), /* err */ - IRQ_ADDR (IRQ_INTSR (1)), /* rx */ - IRQ_ADDR (IRQ_INTST (1)), /* tx */ -#if IRQ_INTSR_NUM > 2 - IRQ_ADDR (IRQ_INTSER (2)), /* err */ - IRQ_ADDR (IRQ_INTSR (2)), /* rx */ - IRQ_ADDR (IRQ_INTST (2)), /* tx */ -#endif -#endif - - -1 -}; - -/* Early initialization for kernel using Multi debugger ROM monitor. */ -void __init multi_init (void) -{ - /* We're using the Multi debugger monitor, so we have to install - the interrupt vectors. The monitor doesn't allow them to be - initially downloaded into their final destination because - it's in the monitor's scratch-RAM area. Unfortunately, Multi - also doesn't deal correctly with ELF sections where the LMA - and VMA differ -- it just ignores the LMA -- so we can't use - that feature to work around the problem. What we do instead - is just put the interrupt vectors into a normal section, and - do the necessary copying and relocation here. Since the - interrupt vector basically only contains `jr' instructions - and no-ops, it's not that hard. */ - extern unsigned long _intv_load_start, _intv_start; - register unsigned long *src = &_intv_load_start; - register unsigned long *dst = (unsigned long *)INTV_BASE; - register unsigned long jr_fixup = (char *)&_intv_start - (char *)dst; - register long *ii; - - /* Copy interrupt vectors as instructed by multi_intv_install_table. */ - for (ii = multi_intv_install_table; *ii >= 0; ii++) { - /* Copy 16-byte interrupt vector at offset *ii. */ - int boffs; - for (boffs = 0; boffs < 0x10; boffs += sizeof *src) { - /* Copy a single word, fixing up the jump offs - if it's a `jr' instruction. */ - int woffs = (*ii + boffs) / sizeof *src; - unsigned long word = src[woffs]; - - if ((word & 0xFC0) == 0x780) { - /* A `jr' insn, fix up its offset (and yes, the - weird half-word swapping is intentional). */ - unsigned short hi = word & 0xFFFF; - unsigned short lo = word >> 16; - unsigned long udisp22 - = lo + ((hi & 0x3F) << 16); - long disp22 = (long)(udisp22 << 10) >> 10; - - disp22 += jr_fixup; - - hi = ((disp22 >> 16) & 0x3F) | 0x780; - lo = disp22 & 0xFFFF; - - word = hi + (lo << 16); - } - - dst[woffs] = word; - } - } -} diff --git a/arch/v850/kernel/rte_ma1_cb-rom.ld b/arch/v850/kernel/rte_ma1_cb-rom.ld deleted file mode 100644 index 87b618f8253b..000000000000 --- a/arch/v850/kernel/rte_ma1_cb-rom.ld +++ /dev/null @@ -1,14 +0,0 @@ -/* Linker script for the Midas labs RTE-V850E/MA1-CB evaluation board - (CONFIG_RTE_CB_MA1), with kernel in ROM. */ - -MEMORY { - ROM : ORIGIN = 0x00000000, LENGTH = 0x00100000 - /* 1MB of SRAM. This memory is mirrored 4 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - /* 32MB of SDRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - ROMK_SECTIONS(ROM, SRAM) -} diff --git a/arch/v850/kernel/rte_ma1_cb.c b/arch/v850/kernel/rte_ma1_cb.c deleted file mode 100644 index 08abf3d5f8df..000000000000 --- a/arch/v850/kernel/rte_ma1_cb.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * arch/v850/kernel/rte_ma1_cb.c -- Midas labs RTE-V850E/MA1-CB board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "mach.h" - - -/* SRAM and SDRAM are almost contiguous (with a small hole in between; - see mach_reserve_bootmem for details), so just use both as one big area. */ -#define RAM_START SRAM_ADDR -#define RAM_END (SDRAM_ADDR + SDRAM_SIZE) - - -void __init mach_early_init (void) -{ - rte_cb_early_init (); -} - -void __init mach_get_physical_ram (unsigned long *ram_start, - unsigned long *ram_len) -{ - *ram_start = RAM_START; - *ram_len = RAM_END - RAM_START; -} - -void __init mach_reserve_bootmem () -{ -#ifdef CONFIG_RTE_CB_MULTI - /* Prevent the kernel from touching the monitor's scratch RAM. */ - reserve_bootmem(MON_SCRATCH_ADDR, MON_SCRATCH_SIZE, - BOOTMEM_DEFAULT); -#endif - - /* The space between SRAM and SDRAM is filled with duplicate - images of SRAM. Prevent the kernel from using them. */ - reserve_bootmem (SRAM_ADDR + SRAM_SIZE, - SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE), - BOOTMEM_DEFAULT); -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -/* Called before configuring an on-chip UART. */ -void rte_ma1_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud) -{ - /* The RTE-MA1-CB connects some general-purpose I/O pins on the - CPU to the RTS/CTS lines of UART 0's serial connection. - I/O pins P42 and P43 are RTS and CTS respectively. */ - if (chan == 0) { - /* Put P42 & P43 in I/O port mode. */ - MA_PORT4_PMC &= ~0xC; - /* Make P42 an output, and P43 an input. */ - MA_PORT4_PM = (MA_PORT4_PM & ~0xC) | 0x8; - } - - /* Do pre-configuration for the actual UART. */ - ma_uart_pre_configure (chan, cflags, baud); -} - -void __init mach_init_irqs (void) -{ - unsigned tc; - - /* Initialize interrupts. */ - ma_init_irqs (); - rte_cb_init_irqs (); - - /* Use falling-edge-sensitivity for interrupts . */ - V850E_TIMER_C_SESC (0) &= ~0xC; - V850E_TIMER_C_SESC (1) &= ~0xF; - - /* INTP000-INTP011 are shared with `Timer C', so we have to set - up Timer C to pass them through as raw interrupts. */ - for (tc = 0; tc < 2; tc++) - /* Turn on the timer. */ - V850E_TIMER_C_TMCC0 (tc) |= V850E_TIMER_C_TMCC0_CAE; - - /* Make sure the relevant port0/port1 pins are assigned - interrupt duty. We used INTP001-INTP011 (don't screw with - INTP000 because the monitor uses it). */ - MA_PORT0_PMC |= 0x4; /* P02 (INTP001) in IRQ mode. */ - MA_PORT1_PMC |= 0x6; /* P11 (INTP010) & P12 (INTP011) in IRQ mode.*/ -} diff --git a/arch/v850/kernel/rte_ma1_cb.ld b/arch/v850/kernel/rte_ma1_cb.ld deleted file mode 100644 index c8e16d16be41..000000000000 --- a/arch/v850/kernel/rte_ma1_cb.ld +++ /dev/null @@ -1,57 +0,0 @@ -/* Linker script for the Midas labs RTE-V850E/MA1-CB evaluation board - (CONFIG_RTE_CB_MA1), with kernel in SDRAM, under Multi debugger. */ - -MEMORY { - /* 1MB of SRAM; we can't use the last 32KB, because it's used by - the monitor scratch-RAM. This memory is mirrored 4 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = (SRAM_SIZE - MON_SCRATCH_SIZE) - /* Monitor scratch RAM; only the interrupt vectors should go here. */ - MRAM : ORIGIN = MON_SCRATCH_ADDR, LENGTH = MON_SCRATCH_SIZE - /* 32MB of SDRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -#ifdef CONFIG_RTE_CB_MA1_KSRAM -# define KRAM SRAM -#else -# define KRAM SDRAM -#endif - -SECTIONS { - /* We can't use RAMK_KRAM_CONTENTS because that puts the whole - kernel in a single ELF segment, and the Multi debugger (which - we use to load the kernel) appears to have bizarre problems - dealing with it. */ - - .text : { - __kram_start = . ; - TEXT_CONTENTS - } > KRAM - - .data : { - DATA_CONTENTS - BSS_CONTENTS - RAMK_INIT_CONTENTS - __kram_end = . ; - BOOTMAP_CONTENTS - - /* The address at which the interrupt vectors are initially - loaded by the loader. We can't load the interrupt vectors - directly into their target location, because the monitor - ROM for the GHS Multi debugger barfs if we try. - Unfortunately, Multi also doesn't deal correctly with ELF - sections where the LMA and VMA differ (it just ignores the - LMA), so we can't use that feature to work around the - problem! What we do instead is just put the interrupt - vectors into a normal section, and have the - `mach_early_init' function for Midas boards do the - necessary copying and relocation at runtime (this section - basically only contains `jr' instructions, so it's not - that hard). */ - . = ALIGN (0x10) ; - __intv_load_start = . ; - INTV_CONTENTS - } > KRAM - - .root ALIGN (4096) : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/rte_mb_a_pci.c b/arch/v850/kernel/rte_mb_a_pci.c deleted file mode 100644 index 687e367d8b64..000000000000 --- a/arch/v850/kernel/rte_mb_a_pci.c +++ /dev/null @@ -1,819 +0,0 @@ -/* - * arch/v850/kernel/mb_a_pci.c -- PCI support for Midas lab RTE-MOTHER-A board - * - * Copyright (C) 2001,02,03,05 NEC Electronics Corporation - * Copyright (C) 2001,02,03,05 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include - -/* __nomods_init is like __devinit, but is a no-op when modules are enabled. - This is used by some routines that can be called either during boot - or by a module. */ -#ifdef CONFIG_MODULES -#define __nomods_init /*nothing*/ -#else -#define __nomods_init __devinit -#endif - -/* PCI devices on the Mother-A board can only do DMA to/from the MB SRAM - (the RTE-V850E/MA1-CB cpu board doesn't support PCI access to - CPU-board memory), and since linux DMA buffers are allocated in - normal kernel memory, we basically have to copy DMA blocks around - (this is like a `bounce buffer'). When a DMA block is `mapped', we - allocate an identically sized block in MB SRAM, and if we're doing - output to the device, copy the CPU-memory block to the MB-SRAM block. - When an active block is `unmapped', we will copy the block back to - CPU memory if necessary, and then deallocate the MB SRAM block. - Ack. */ - -/* Where the motherboard SRAM is in the PCI-bus address space (the - first 512K of it is also mapped at PCI address 0). */ -#define PCI_MB_SRAM_ADDR 0x800000 - -/* Convert CPU-view MB SRAM address to/from PCI-view addresses of the - same memory. */ -#define MB_SRAM_TO_PCI(mb_sram_addr) \ - ((dma_addr_t)mb_sram_addr - MB_A_SRAM_ADDR + PCI_MB_SRAM_ADDR) -#define PCI_TO_MB_SRAM(pci_addr) \ - (void *)(pci_addr - PCI_MB_SRAM_ADDR + MB_A_SRAM_ADDR) - -static void pcibios_assign_resources (void); - -struct mb_pci_dev_irq { - unsigned dev; /* PCI device number */ - unsigned irq_base; /* First IRQ */ - unsigned query_pin; /* True if we should read the device's - Interrupt Pin info, and allocate - interrupt IRQ_BASE + PIN. */ -}; - -/* PCI interrupts are mapped statically to GBUS interrupts. */ -static struct mb_pci_dev_irq mb_pci_dev_irqs[] = { - /* Motherboard SB82558 ethernet controller */ - { 10, IRQ_MB_A_LAN, 0 }, - /* PCI slot 1 */ - { 8, IRQ_MB_A_PCI1(0), 1 }, - /* PCI slot 2 */ - { 9, IRQ_MB_A_PCI2(0), 1 } -}; -#define NUM_MB_PCI_DEV_IRQS ARRAY_SIZE(mb_pci_dev_irqs) - - -/* PCI configuration primitives. */ - -#define CONFIG_DMCFGA(bus, devfn, offs) \ - (0x80000000 \ - | ((offs) & ~0x3) \ - | ((devfn) << 8) \ - | ((bus)->number << 16)) - -static int -mb_pci_read (struct pci_bus *bus, unsigned devfn, int offs, int size, u32 *rval) -{ - u32 addr; - int flags; - - local_irq_save (flags); - - MB_A_PCI_PCICR = 0x7; - MB_A_PCI_DMCFGA = CONFIG_DMCFGA (bus, devfn, offs); - - addr = MB_A_PCI_IO_ADDR + (offs & 0x3); - - switch (size) { - case 1: *rval = *(volatile u8 *)addr; break; - case 2: *rval = *(volatile u16 *)addr; break; - case 4: *rval = *(volatile u32 *)addr; break; - } - - if (MB_A_PCI_PCISR & 0x2000) { - MB_A_PCI_PCISR = 0x2000; - *rval = ~0; - } - - MB_A_PCI_DMCFGA = 0; - - local_irq_restore (flags); - - return PCIBIOS_SUCCESSFUL; -} - -static int -mb_pci_write (struct pci_bus *bus, unsigned devfn, int offs, int size, u32 val) -{ - u32 addr; - int flags; - - local_irq_save (flags); - - MB_A_PCI_PCICR = 0x7; - MB_A_PCI_DMCFGA = CONFIG_DMCFGA (bus, devfn, offs); - - addr = MB_A_PCI_IO_ADDR + (offs & 0x3); - - switch (size) { - case 1: *(volatile u8 *)addr = val; break; - case 2: *(volatile u16 *)addr = val; break; - case 4: *(volatile u32 *)addr = val; break; - } - - if (MB_A_PCI_PCISR & 0x2000) - MB_A_PCI_PCISR = 0x2000; - - MB_A_PCI_DMCFGA = 0; - - local_irq_restore (flags); - - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops mb_pci_config_ops = { - .read = mb_pci_read, - .write = mb_pci_write, -}; - - -/* PCI Initialization. */ - -static struct pci_bus *mb_pci_bus = 0; - -/* Do initial PCI setup. */ -static int __devinit pcibios_init (void) -{ - u32 id = MB_A_PCI_PCIHIDR; - u16 vendor = id & 0xFFFF; - u16 device = (id >> 16) & 0xFFFF; - - if (vendor == PCI_VENDOR_ID_PLX && device == PCI_DEVICE_ID_PLX_9080) { - printk (KERN_INFO - "PCI: PLX Technology PCI9080 HOST/PCI bridge\n"); - - MB_A_PCI_PCICR = 0x147; - - MB_A_PCI_PCIBAR0 = 0x007FFF00; - MB_A_PCI_PCIBAR1 = 0x0000FF00; - MB_A_PCI_PCIBAR2 = 0x00800000; - - MB_A_PCI_PCILTR = 0x20; - - MB_A_PCI_PCIPBAM |= 0x3; - - MB_A_PCI_PCISR = ~0; /* Clear errors. */ - - /* Reprogram the motherboard's IO/config address space, - as we don't support the GCS7 address space that the - default uses. */ - - /* Significant address bits used for decoding PCI GCS5 space - accesses. */ - MB_A_PCI_DMRR = ~(MB_A_PCI_MEM_SIZE - 1); - - /* I don't understand this, but the SolutionGear example code - uses such an offset, and it doesn't work without it. XXX */ -#if GCS5_SIZE == 0x00800000 -#define GCS5_CFG_OFFS 0x00800000 -#else -#define GCS5_CFG_OFFS 0 -#endif - - /* Address bit values for matching. Note that we have to give - the address from the motherboard's point of view, which is - different than the CPU's. */ - /* PCI memory space. */ - MB_A_PCI_DMLBAM = GCS5_CFG_OFFS + 0x0; - /* PCI I/O space. */ - MB_A_PCI_DMLBAI = - GCS5_CFG_OFFS + (MB_A_PCI_IO_ADDR - GCS5_ADDR); - - mb_pci_bus = pci_scan_bus (0, &mb_pci_config_ops, 0); - - pcibios_assign_resources (); - } else - printk (KERN_ERR "PCI: HOST/PCI bridge not found\n"); - - return 0; -} - -subsys_initcall (pcibios_init); - -char __devinit *pcibios_setup (char *option) -{ - /* Don't handle any options. */ - return option; -} - - -int __nomods_init pcibios_enable_device (struct pci_dev *dev, int mask) -{ - u16 cmd, old_cmd; - int idx; - struct resource *r; - - pci_read_config_word(dev, PCI_COMMAND, &cmd); - old_cmd = cmd; - for (idx = 0; idx < 6; idx++) { - r = &dev->resource[idx]; - if (!r->start && r->end) { - printk(KERN_ERR "PCI: Device %s not available because " - "of resource collisions\n", pci_name(dev)); - return -EINVAL; - } - if (r->flags & IORESOURCE_IO) - cmd |= PCI_COMMAND_IO; - if (r->flags & IORESOURCE_MEM) - cmd |= PCI_COMMAND_MEMORY; - } - if (cmd != old_cmd) { - printk("PCI: Enabling device %s (%04x -> %04x)\n", - pci_name(dev), old_cmd, cmd); - pci_write_config_word(dev, PCI_COMMAND, cmd); - } - return 0; -} - - -/* Resource allocation. */ -static void __devinit pcibios_assign_resources (void) -{ - struct pci_dev *dev = NULL; - struct resource *r; - - for_each_pci_dev(dev) { - unsigned di_num; - unsigned class = dev->class >> 8; - - if (class && class != PCI_CLASS_BRIDGE_HOST) { - unsigned r_num; - for(r_num = 0; r_num < 6; r_num++) { - r = &dev->resource[r_num]; - if (!r->start && r->end) - pci_assign_resource (dev, r_num); - } - } - - /* Assign interrupts. */ - for (di_num = 0; di_num < NUM_MB_PCI_DEV_IRQS; di_num++) { - struct mb_pci_dev_irq *di = &mb_pci_dev_irqs[di_num]; - - if (di->dev == PCI_SLOT (dev->devfn)) { - unsigned irq = di->irq_base; - - if (di->query_pin) { - /* Find out which interrupt pin - this device uses (each PCI - slot has 4). */ - u8 irq_pin; - - pci_read_config_byte (dev, - PCI_INTERRUPT_PIN, - &irq_pin); - - if (irq_pin == 0) - /* Doesn't use interrupts. */ - continue; - else - irq += irq_pin - 1; - } - - pcibios_update_irq (dev, irq); - } - } - } -} - -void __devinit pcibios_update_irq (struct pci_dev *dev, int irq) -{ - dev->irq = irq; - pci_write_config_byte (dev, PCI_INTERRUPT_LINE, irq); -} - -void __devinit -pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, - struct resource *res) -{ - unsigned long offset = 0; - - if (res->flags & IORESOURCE_IO) { - offset = MB_A_PCI_IO_ADDR; - } else if (res->flags & IORESOURCE_MEM) { - offset = MB_A_PCI_MEM_ADDR; - } - - region->start = res->start - offset; - region->end = res->end - offset; -} - - -/* Stubs for things we don't use. */ - -/* Called after each bus is probed, but before its children are examined. */ -void pcibios_fixup_bus(struct pci_bus *b) -{ -} - -void -pcibios_align_resource (void *data, struct resource *res, - resource_size_t size, resource_size_t align) -{ -} - -void pcibios_set_master (struct pci_dev *dev) -{ -} - - -/* Mother-A SRAM memory allocation. This is a simple first-fit allocator. */ - -/* A memory free-list node. */ -struct mb_sram_free_area { - void *mem; - unsigned long size; - struct mb_sram_free_area *next; -}; - -/* The tail of the free-list, which starts out containing all the SRAM. */ -static struct mb_sram_free_area mb_sram_free_tail = { - (void *)MB_A_SRAM_ADDR, MB_A_SRAM_SIZE, 0 -}; - -/* The free-list. */ -static struct mb_sram_free_area *mb_sram_free_areas = &mb_sram_free_tail; - -/* The free-list of free free-list nodes. (:-) */ -static struct mb_sram_free_area *mb_sram_free_free_areas = 0; - -/* Spinlock protecting the above globals. */ -static DEFINE_SPINLOCK(mb_sram_lock); - -/* Allocate a memory block at least SIZE bytes long in the Mother-A SRAM - space. */ -static void *alloc_mb_sram (size_t size) -{ - struct mb_sram_free_area *prev, *fa; - unsigned long flags; - void *mem = 0; - - spin_lock_irqsave (mb_sram_lock, flags); - - /* Look for a free area that can contain SIZE bytes. */ - for (prev = 0, fa = mb_sram_free_areas; fa; prev = fa, fa = fa->next) - if (fa->size >= size) { - /* Found one! */ - mem = fa->mem; - - if (fa->size == size) { - /* In fact, it fits exactly, so remove - this node from the free-list. */ - if (prev) - prev->next = fa->next; - else - mb_sram_free_areas = fa->next; - /* Put it on the free-list-entry-free-list. */ - fa->next = mb_sram_free_free_areas; - mb_sram_free_free_areas = fa; - } else { - /* FA is bigger than SIZE, so just - reduce its size to account for this - allocation. */ - fa->mem += size; - fa->size -= size; - } - - break; - } - - spin_unlock_irqrestore (mb_sram_lock, flags); - - return mem; -} - -/* Return the memory area MEM of size SIZE to the MB SRAM free pool. */ -static void free_mb_sram (void *mem, size_t size) -{ - struct mb_sram_free_area *prev, *fa, *new_fa; - unsigned long flags; - void *end = mem + size; - - spin_lock_irqsave (mb_sram_lock, flags); - - retry: - /* Find an adjacent free-list entry. */ - for (prev = 0, fa = mb_sram_free_areas; fa; prev = fa, fa = fa->next) - if (fa->mem == end) { - /* FA is just after MEM, grow down to encompass it. */ - fa->mem = mem; - fa->size += size; - goto done; - } else if (fa->mem + fa->size == mem) { - struct mb_sram_free_area *next_fa = fa->next; - - /* FA is just before MEM, expand to encompass it. */ - fa->size += size; - - /* See if FA can now be merged with its successor. */ - if (next_fa && fa->mem + fa->size == next_fa->mem) { - /* Yup; merge NEXT_FA's info into FA. */ - fa->size += next_fa->size; - fa->next = next_fa->next; - /* Free NEXT_FA. */ - next_fa->next = mb_sram_free_free_areas; - mb_sram_free_free_areas = next_fa; - } - goto done; - } else if (fa->mem > mem) - /* We've reached the right spot in the free-list - without finding an adjacent free-area, so add - a new free area to hold mem. */ - break; - - /* Make a new free-list entry. */ - - /* First, get a free-list entry. */ - if (! mb_sram_free_free_areas) { - /* There are none, so make some. */ - void *block; - size_t block_size = sizeof (struct mb_sram_free_area) * 8; - - /* Don't hold the lock while calling kmalloc (I'm not - sure whether it would be a problem, since we use - GFP_ATOMIC, but it makes me nervous). */ - spin_unlock_irqrestore (mb_sram_lock, flags); - - block = kmalloc (block_size, GFP_ATOMIC); - if (! block) - panic ("free_mb_sram: can't allocate free-list entry"); - - /* Now get the lock back. */ - spin_lock_irqsave (mb_sram_lock, flags); - - /* Add the new free free-list entries. */ - while (block_size > 0) { - struct mb_sram_free_area *nfa = block; - nfa->next = mb_sram_free_free_areas; - mb_sram_free_free_areas = nfa; - block += sizeof *nfa; - block_size -= sizeof *nfa; - } - - /* Since we dropped the lock to call kmalloc, the - free-list could have changed, so retry from the - beginning. */ - goto retry; - } - - /* Remove NEW_FA from the free-list of free-list entries. */ - new_fa = mb_sram_free_free_areas; - mb_sram_free_free_areas = new_fa->next; - - /* NEW_FA initially holds only MEM. */ - new_fa->mem = mem; - new_fa->size = size; - - /* Insert NEW_FA in the free-list between PREV and FA. */ - new_fa->next = fa; - if (prev) - prev->next = new_fa; - else - mb_sram_free_areas = new_fa; - - done: - spin_unlock_irqrestore (mb_sram_lock, flags); -} - - -/* Maintainence of CPU -> Mother-A DMA mappings. */ - -struct dma_mapping { - void *cpu_addr; - void *mb_sram_addr; - size_t size; - struct dma_mapping *next; -}; - -/* A list of mappings from CPU addresses to MB SRAM addresses for active - DMA blocks (that have been `granted' to the PCI device). */ -static struct dma_mapping *active_dma_mappings = 0; - -/* A list of free mapping objects. */ -static struct dma_mapping *free_dma_mappings = 0; - -/* Spinlock protecting the above globals. */ -static DEFINE_SPINLOCK(dma_mappings_lock); - -static struct dma_mapping *new_dma_mapping (size_t size) -{ - unsigned long flags; - struct dma_mapping *mapping; - void *mb_sram_block = alloc_mb_sram (size); - - if (! mb_sram_block) - return 0; - - spin_lock_irqsave (dma_mappings_lock, flags); - - if (! free_dma_mappings) { - /* We're out of mapping structures, make more. */ - void *mblock; - size_t mblock_size = sizeof (struct dma_mapping) * 8; - - /* Don't hold the lock while calling kmalloc (I'm not - sure whether it would be a problem, since we use - GFP_ATOMIC, but it makes me nervous). */ - spin_unlock_irqrestore (dma_mappings_lock, flags); - - mblock = kmalloc (mblock_size, GFP_ATOMIC); - if (! mblock) { - free_mb_sram (mb_sram_block, size); - return 0; - } - - /* Get the lock back. */ - spin_lock_irqsave (dma_mappings_lock, flags); - - /* Add the new mapping structures to the free-list. */ - while (mblock_size > 0) { - struct dma_mapping *fm = mblock; - fm->next = free_dma_mappings; - free_dma_mappings = fm; - mblock += sizeof *fm; - mblock_size -= sizeof *fm; - } - } - - /* Get a mapping struct from the freelist. */ - mapping = free_dma_mappings; - free_dma_mappings = mapping->next; - - /* Initialize the mapping. Other fields should be filled in by - caller. */ - mapping->mb_sram_addr = mb_sram_block; - mapping->size = size; - - /* Add it to the list of active mappings. */ - mapping->next = active_dma_mappings; - active_dma_mappings = mapping; - - spin_unlock_irqrestore (dma_mappings_lock, flags); - - return mapping; -} - -static struct dma_mapping *find_dma_mapping (void *mb_sram_addr) -{ - unsigned long flags; - struct dma_mapping *mapping; - - spin_lock_irqsave (dma_mappings_lock, flags); - - for (mapping = active_dma_mappings; mapping; mapping = mapping->next) - if (mapping->mb_sram_addr == mb_sram_addr) { - spin_unlock_irqrestore (dma_mappings_lock, flags); - return mapping; - } - - panic ("find_dma_mapping: unmapped PCI DMA addr 0x%x", - MB_SRAM_TO_PCI (mb_sram_addr)); -} - -static struct dma_mapping *deactivate_dma_mapping (void *mb_sram_addr) -{ - unsigned long flags; - struct dma_mapping *mapping, *prev; - - spin_lock_irqsave (dma_mappings_lock, flags); - - for (prev = 0, mapping = active_dma_mappings; - mapping; - prev = mapping, mapping = mapping->next) - { - if (mapping->mb_sram_addr == mb_sram_addr) { - /* This is the MAPPING; deactivate it. */ - if (prev) - prev->next = mapping->next; - else - active_dma_mappings = mapping->next; - - spin_unlock_irqrestore (dma_mappings_lock, flags); - - return mapping; - } - } - - panic ("deactivate_dma_mapping: unmapped PCI DMA addr 0x%x", - MB_SRAM_TO_PCI (mb_sram_addr)); -} - -/* Return MAPPING to the freelist. */ -static inline void -free_dma_mapping (struct dma_mapping *mapping) -{ - unsigned long flags; - - free_mb_sram (mapping->mb_sram_addr, mapping->size); - - spin_lock_irqsave (dma_mappings_lock, flags); - - mapping->next = free_dma_mappings; - free_dma_mappings = mapping; - - spin_unlock_irqrestore (dma_mappings_lock, flags); -} - - -/* Single PCI DMA mappings. */ - -/* `Grant' to PDEV the memory block at CPU_ADDR, for doing DMA. The - 32-bit PCI bus mastering address to use is returned. the device owns - this memory until either pci_unmap_single or pci_dma_sync_single is - performed. */ -dma_addr_t -pci_map_single (struct pci_dev *pdev, void *cpu_addr, size_t size, int dir) -{ - struct dma_mapping *mapping = new_dma_mapping (size); - - if (! mapping) - return 0; - - mapping->cpu_addr = cpu_addr; - - if (dir == PCI_DMA_BIDIRECTIONAL || dir == PCI_DMA_TODEVICE) - memcpy (mapping->mb_sram_addr, cpu_addr, size); - - return MB_SRAM_TO_PCI (mapping->mb_sram_addr); -} - -/* Return to the CPU the PCI DMA memory block previously `granted' to - PDEV, at DMA_ADDR. */ -void pci_unmap_single (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, - int dir) -{ - void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr); - struct dma_mapping *mapping = deactivate_dma_mapping (mb_sram_addr); - - if (size != mapping->size) - panic ("pci_unmap_single: size (%d) doesn't match" - " size of mapping at PCI DMA addr 0x%x (%d)\n", - size, dma_addr, mapping->size); - - /* Copy back the DMA'd contents if necessary. */ - if (dir == PCI_DMA_BIDIRECTIONAL || dir == PCI_DMA_FROMDEVICE) - memcpy (mapping->cpu_addr, mb_sram_addr, size); - - /* Return mapping to the freelist. */ - free_dma_mapping (mapping); -} - -/* Make physical memory consistent for a single streaming mode DMA - translation after a transfer. - - If you perform a pci_map_single() but wish to interrogate the - buffer using the cpu, yet do not wish to teardown the PCI dma - mapping, you must call this function before doing so. At the next - point you give the PCI dma address back to the card, you must first - perform a pci_dma_sync_for_device, and then the device again owns - the buffer. */ -void -pci_dma_sync_single_for_cpu (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, - int dir) -{ - void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr); - struct dma_mapping *mapping = find_dma_mapping (mb_sram_addr); - - /* Synchronize the DMA buffer with the CPU buffer if necessary. */ - if (dir == PCI_DMA_FROMDEVICE) - memcpy (mapping->cpu_addr, mb_sram_addr, size); - else if (dir == PCI_DMA_TODEVICE) - ; /* nothing to do */ - else - panic("pci_dma_sync_single: unsupported sync dir: %d", dir); -} - -void -pci_dma_sync_single_for_device (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, - int dir) -{ - void *mb_sram_addr = PCI_TO_MB_SRAM (dma_addr); - struct dma_mapping *mapping = find_dma_mapping (mb_sram_addr); - - /* Synchronize the DMA buffer with the CPU buffer if necessary. */ - if (dir == PCI_DMA_FROMDEVICE) - ; /* nothing to do */ - else if (dir == PCI_DMA_TODEVICE) - memcpy (mb_sram_addr, mapping->cpu_addr, size); - else - panic("pci_dma_sync_single: unsupported sync dir: %d", dir); -} - - -/* Scatter-gather PCI DMA mappings. */ - -/* Do multiple DMA mappings at once. */ -int -pci_map_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len, int dir) -{ - BUG (); - return 0; -} - -/* Unmap multiple DMA mappings at once. */ -void -pci_unmap_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len,int dir) -{ - BUG (); -} - -/* Make physical memory consistent for a set of streaming mode DMA - translations after a transfer. The same as pci_dma_sync_single_* but - for a scatter-gather list, same rules and usage. */ - -void -pci_dma_sync_sg_for_cpu (struct pci_dev *dev, - struct scatterlist *sg, int sg_len, - int dir) -{ - BUG (); -} - -void -pci_dma_sync_sg_for_device (struct pci_dev *dev, - struct scatterlist *sg, int sg_len, - int dir) -{ - BUG (); -} - - -/* PCI mem mapping. */ - -/* Allocate and map kernel buffer using consistent mode DMA for PCI - device. Returns non-NULL cpu-view pointer to the buffer if - successful and sets *DMA_ADDR to the pci side dma address as well, - else DMA_ADDR is undefined. */ -void * -pci_alloc_consistent (struct pci_dev *pdev, size_t size, dma_addr_t *dma_addr) -{ - void *mb_sram_mem = alloc_mb_sram (size); - if (mb_sram_mem) - *dma_addr = MB_SRAM_TO_PCI (mb_sram_mem); - return mb_sram_mem; -} - -/* Free and unmap a consistent DMA buffer. CPU_ADDR and DMA_ADDR must - be values that were returned from pci_alloc_consistent. SIZE must be - the same as what as passed into pci_alloc_consistent. References to - the memory and mappings associated with CPU_ADDR or DMA_ADDR past - this call are illegal. */ -void -pci_free_consistent (struct pci_dev *pdev, size_t size, void *cpu_addr, - dma_addr_t dma_addr) -{ - void *mb_sram_mem = PCI_TO_MB_SRAM (dma_addr); - free_mb_sram (mb_sram_mem, size); -} - - -/* iomap/iomap */ - -void __iomem *pci_iomap (struct pci_dev *dev, int bar, unsigned long max) -{ - resource_size_t start = pci_resource_start (dev, bar); - resource_size_t len = pci_resource_len (dev, bar); - - if (!start || len == 0) - return 0; - - /* None of the ioremap functions actually do anything, other than - re-casting their argument, so don't bother differentiating them. */ - return ioremap (start, len); -} - -void pci_iounmap (struct pci_dev *dev, void __iomem *addr) -{ - /* nothing */ -} - - -/* symbol exports (for modules) */ - -EXPORT_SYMBOL (pci_map_single); -EXPORT_SYMBOL (pci_unmap_single); -EXPORT_SYMBOL (pci_alloc_consistent); -EXPORT_SYMBOL (pci_free_consistent); -EXPORT_SYMBOL (pci_dma_sync_single_for_cpu); -EXPORT_SYMBOL (pci_dma_sync_single_for_device); -EXPORT_SYMBOL (pci_iomap); -EXPORT_SYMBOL (pci_iounmap); diff --git a/arch/v850/kernel/rte_me2_cb.c b/arch/v850/kernel/rte_me2_cb.c deleted file mode 100644 index 46803d48dffe..000000000000 --- a/arch/v850/kernel/rte_me2_cb.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * arch/v850/kernel/rte_me2_cb.c -- Midas labs RTE-V850E/ME2-CB board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mach.h" - -extern unsigned long *_intv_start; -extern unsigned long *_intv_end; - -/* LED access routines. */ -extern unsigned read_leds (int pos, char *buf, int len); -extern unsigned write_leds (int pos, const char *buf, int len); - - -/* SDRAM are almost contiguous (with a small hole in between; - see mach_reserve_bootmem for details), so just use both as one big area. */ -#define RAM_START SDRAM_ADDR -#define RAM_END (SDRAM_ADDR + SDRAM_SIZE) - - -void __init mach_get_physical_ram (unsigned long *ram_start, - unsigned long *ram_len) -{ - *ram_start = RAM_START; - *ram_len = RAM_END - RAM_START; -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -/* Called before configuring an on-chip UART. */ -void rte_me2_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud) -{ - /* The RTE-V850E/ME2-CB connects some general-purpose I/O - pins on the CPU to the RTS/CTS lines of UARTB channel 0's - serial connection. - I/O pins P21 and P22 are RTS and CTS respectively. */ - if (chan == 0) { - /* Put P21 & P22 in I/O port mode. */ - ME2_PORT2_PMC &= ~0x6; - /* Make P21 and output, and P22 an input. */ - ME2_PORT2_PM = (ME2_PORT2_PM & ~0xC) | 0x4; - } - - me2_uart_pre_configure (chan, cflags, baud); -} - -void __init mach_init_irqs (void) -{ - /* Initialize interrupts. */ - me2_init_irqs (); - rte_me2_cb_init_irqs (); -} - -#ifdef CONFIG_ROM_KERNEL -/* Initialization for kernel in ROM. */ -static inline rom_kernel_init (void) -{ - /* If the kernel is in ROM, we have to copy any initialized data - from ROM into RAM. */ - extern unsigned long _data_load_start, _sdata, _edata; - register unsigned long *src = &_data_load_start; - register unsigned long *dst = &_sdata, *end = &_edata; - - while (dst != end) - *dst++ = *src++; -} -#endif /* CONFIG_ROM_KERNEL */ - -static void install_interrupt_vectors (void) -{ - unsigned long *p1, *p2; - - ME2_IRAMM = 0x03; /* V850E/ME2 iRAM write mode */ - - /* vector copy to iRAM */ - p1 = (unsigned long *)0; /* v85x vector start */ - p2 = (unsigned long *)&_intv_start; - while (p2 < (unsigned long *)&_intv_end) - *p1++ = *p2++; - - ME2_IRAMM = 0x00; /* V850E/ME2 iRAM read mode */ -} - -/* CompactFlash */ - -static void cf_power_on (void) -{ - /* CF card detected? */ - if (CB_CF_STS0 & 0x0030) - return; - - CB_CF_REG0 = 0x0002; /* reest on */ - mdelay (10); - CB_CF_REG0 = 0x0003; /* power on */ - mdelay (10); - CB_CF_REG0 = 0x0001; /* reset off */ - mdelay (10); -} - -static void cf_power_off (void) -{ - CB_CF_REG0 = 0x0003; /* power on */ - mdelay (10); - CB_CF_REG0 = 0x0002; /* reest on */ - mdelay (10); -} - -void __init mach_early_init (void) -{ - install_interrupt_vectors (); - - /* CS1 SDRAM instruction cache enable */ - v850e_cache_enable (0x04, 0x03, 0); - - rte_cb_early_init (); - - /* CompactFlash power on */ - cf_power_on (); - -#if defined (CONFIG_ROM_KERNEL) - rom_kernel_init (); -#endif -} - - -/* RTE-V850E/ME2-CB Programmable Interrupt Controller. */ - -static struct cb_pic_irq_init cb_pic_irq_inits[] = { - { "CB_EXTTM0", IRQ_CB_EXTTM0, 1, 1, 6 }, - { "CB_EXTSIO", IRQ_CB_EXTSIO, 1, 1, 6 }, - { "CB_TOVER", IRQ_CB_TOVER, 1, 1, 6 }, - { "CB_GINT0", IRQ_CB_GINT0, 1, 1, 6 }, - { "CB_USB", IRQ_CB_USB, 1, 1, 6 }, - { "CB_LANC", IRQ_CB_LANC, 1, 1, 6 }, - { "CB_USB_VBUS_ON", IRQ_CB_USB_VBUS_ON, 1, 1, 6 }, - { "CB_USB_VBUS_OFF", IRQ_CB_USB_VBUS_OFF, 1, 1, 6 }, - { "CB_EXTTM1", IRQ_CB_EXTTM1, 1, 1, 6 }, - { "CB_EXTTM2", IRQ_CB_EXTTM2, 1, 1, 6 }, - { 0 } -}; -#define NUM_CB_PIC_IRQ_INITS (ARRAY_SIZE(cb_pic_irq_inits) - 1) - -static struct hw_interrupt_type cb_pic_hw_itypes[NUM_CB_PIC_IRQ_INITS]; -static unsigned char cb_pic_active_irqs = 0; - -void __init rte_me2_cb_init_irqs (void) -{ - cb_pic_init_irq_types (cb_pic_irq_inits, cb_pic_hw_itypes); - - /* Initalize on board PIC1 (not PIC0) enable */ - CB_PIC_INT0M = 0x0000; - CB_PIC_INT1M = 0x0000; - CB_PIC_INTR = 0x0000; - CB_PIC_INTEN |= CB_PIC_INT1EN; - - ME2_PORT2_PMC |= 0x08; /* INTP23/SCK1 mode */ - ME2_PORT2_PFC &= ~0x08; /* INTP23 mode */ - ME2_INTR(2) &= ~0x08; /* INTP23 falling-edge detect */ - ME2_INTF(2) &= ~0x08; /* " */ - - rte_cb_init_irqs (); /* gbus &c */ -} - - -/* Enable interrupt handling for interrupt IRQ. */ -void cb_pic_enable_irq (unsigned irq) -{ - CB_PIC_INT1M |= 1 << (irq - CB_PIC_BASE_IRQ); -} - -void cb_pic_disable_irq (unsigned irq) -{ - CB_PIC_INT1M &= ~(1 << (irq - CB_PIC_BASE_IRQ)); -} - -void cb_pic_shutdown_irq (unsigned irq) -{ - cb_pic_disable_irq (irq); - - if (--cb_pic_active_irqs == 0) - free_irq (IRQ_CB_PIC, 0); - - CB_PIC_INT1M &= ~(1 << (irq - CB_PIC_BASE_IRQ)); -} - -static irqreturn_t cb_pic_handle_irq (int irq, void *dev_id, - struct pt_regs *regs) -{ - irqreturn_t rval = IRQ_NONE; - unsigned status = CB_PIC_INTR; - unsigned enable = CB_PIC_INT1M; - - /* Only pay attention to enabled interrupts. */ - status &= enable; - - CB_PIC_INTEN &= ~CB_PIC_INT1EN; - - if (status) { - unsigned mask = 1; - - irq = CB_PIC_BASE_IRQ; - do { - /* There's an active interrupt, find out which one, - and call its handler. */ - while (! (status & mask)) { - irq++; - mask <<= 1; - } - status &= ~mask; - - CB_PIC_INTR = mask; - - /* Recursively call handle_irq to handle it. */ - handle_irq (irq, regs); - rval = IRQ_HANDLED; - } while (status); - } - - CB_PIC_INTEN |= CB_PIC_INT1EN; - - return rval; -} - - -static void irq_nop (unsigned irq) { } - -static unsigned cb_pic_startup_irq (unsigned irq) -{ - int rval; - - if (cb_pic_active_irqs == 0) { - rval = request_irq (IRQ_CB_PIC, cb_pic_handle_irq, - IRQF_DISABLED, "cb_pic_handler", 0); - if (rval != 0) - return rval; - } - - cb_pic_active_irqs++; - - cb_pic_enable_irq (irq); - - return 0; -} - -/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array - INITS (which is terminated by an entry with the name field == 0). */ -void __init cb_pic_init_irq_types (struct cb_pic_irq_init *inits, - struct hw_interrupt_type *hw_irq_types) -{ - struct cb_pic_irq_init *init; - for (init = inits; init->name; init++) { - struct hw_interrupt_type *hwit = hw_irq_types++; - - hwit->typename = init->name; - - hwit->startup = cb_pic_startup_irq; - hwit->shutdown = cb_pic_shutdown_irq; - hwit->enable = cb_pic_enable_irq; - hwit->disable = cb_pic_disable_irq; - hwit->ack = irq_nop; - hwit->end = irq_nop; - - /* Initialize kernel IRQ infrastructure for this interrupt. */ - init_irq_handlers(init->base, init->num, init->interval, hwit); - } -} diff --git a/arch/v850/kernel/rte_me2_cb.ld b/arch/v850/kernel/rte_me2_cb.ld deleted file mode 100644 index cf0766065ec6..000000000000 --- a/arch/v850/kernel/rte_me2_cb.ld +++ /dev/null @@ -1,30 +0,0 @@ -/* Linker script for the Midas labs RTE-V850E/ME2-CB evaluation board - (CONFIG_RTE_CB_ME2), with kernel in SDRAM. */ - -MEMORY { - /* 128Kbyte of IRAM */ - IRAM : ORIGIN = 0x00000000, LENGTH = 0x00020000 - - /* 32MB of SDRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -#define KRAM SDRAM - -SECTIONS { - .text : { - __kram_start = . ; - TEXT_CONTENTS - INTV_CONTENTS /* copy to iRAM (0x0-0x620) */ - } > KRAM - - .data : { - DATA_CONTENTS - BSS_CONTENTS - RAMK_INIT_CONTENTS - __kram_end = . ; - BOOTMAP_CONTENTS - } > KRAM - - .root ALIGN (4096) : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/rte_nb85e_cb-multi.ld b/arch/v850/kernel/rte_nb85e_cb-multi.ld deleted file mode 100644 index de347b4fffac..000000000000 --- a/arch/v850/kernel/rte_nb85e_cb-multi.ld +++ /dev/null @@ -1,57 +0,0 @@ -/* Linker script for the Midas labs RTE-NB85E-CB evaluation board - (CONFIG_RTE_CB_NB85E), with the Multi debugger ROM monitor . */ - -MEMORY { - /* 1MB of SRAM; we can't use the last 96KB, because it's used by - the monitor scratch-RAM. This memory is mirrored 4 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = (SRAM_SIZE - MON_SCRATCH_SIZE) - /* Monitor scratch RAM; only the interrupt vectors should go here. */ - MRAM : ORIGIN = MON_SCRATCH_ADDR, LENGTH = MON_SCRATCH_SIZE - /* 16MB of SDRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -#ifdef CONFIG_RTE_CB_NB85E_KSRAM -# define KRAM SRAM -#else -# define KRAM SDRAM -#endif - -SECTIONS { - /* We can't use RAMK_KRAM_CONTENTS because that puts the whole - kernel in a single ELF segment, and the Multi debugger (which - we use to load the kernel) appears to have bizarre problems - dealing with it. */ - - .text : { - __kram_start = . ; - TEXT_CONTENTS - } > KRAM - - .data : { - DATA_CONTENTS - BSS_CONTENTS - RAMK_INIT_CONTENTS - __kram_end = . ; - BOOTMAP_CONTENTS - - /* The address at which the interrupt vectors are initially - loaded by the loader. We can't load the interrupt vectors - directly into their target location, because the monitor - ROM for the GHS Multi debugger barfs if we try. - Unfortunately, Multi also doesn't deal correctly with ELF - sections where the LMA and VMA differ (it just ignores the - LMA), so we can't use that feature to work around the - problem! What we do instead is just put the interrupt - vectors into a normal section, and have the - `mach_early_init' function for Midas boards do the - necessary copying and relocation at runtime (this section - basically only contains `jr' instructions, so it's not - that hard). */ - . = ALIGN (0x10) ; - __intv_load_start = . ; - INTV_CONTENTS - } > KRAM - - .root ALIGN (4096) : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/rte_nb85e_cb.c b/arch/v850/kernel/rte_nb85e_cb.c deleted file mode 100644 index b4a045da5d70..000000000000 --- a/arch/v850/kernel/rte_nb85e_cb.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * arch/v850/kernel/rte_nb85e_cb.c -- Midas labs RTE-V850E/NB85E-CB board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mach.h" - -void __init mach_early_init (void) -{ - /* Configure caching; some possible settings: - - BHC = 0x0000, DCC = 0x0000 -- all caching disabled - BHC = 0x0040, DCC = 0x0000 -- SDRAM: icache only - BHC = 0x0080, DCC = 0x0C00 -- SDRAM: write-back dcache only - BHC = 0x00C0, DCC = 0x0C00 -- SDRAM: icache + write-back dcache - BHC = 0x00C0, DCC = 0x0800 -- SDRAM: icache + write-thru dcache - - We can only cache SDRAM (we can't use cache SRAM because it's in - the same memory region as the on-chip RAM and I/O space). - - Unfortunately, the dcache seems to be buggy, so we only use the - icache for now. */ - v850e_cache_enable (0x0040 /*BHC*/, 0x0003 /*ICC*/, 0x0000 /*DCC*/); - - rte_cb_early_init (); -} - -void __init mach_get_physical_ram (unsigned long *ram_start, - unsigned long *ram_len) -{ - /* We just use SDRAM here. */ - *ram_start = SDRAM_ADDR; - *ram_len = SDRAM_SIZE; -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -/* Called before configuring an on-chip UART. */ -void rte_nb85e_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud) -{ - /* The RTE-NB85E-CB connects some general-purpose I/O pins on the - CPU to the RTS/CTS lines the UART's serial connection, as follows: - P00 = CTS (in), P01 = DSR (in), P02 = RTS (out), P03 = DTR (out). */ - - TEG_PORT0_PM = 0x03; /* P00 and P01 inputs, P02 and P03 outputs */ - TEG_PORT0_IO = 0x03; /* Accept input */ - - /* Do pre-configuration for the actual UART. */ - teg_uart_pre_configure (chan, cflags, baud); -} - -void __init mach_init_irqs (void) -{ - teg_init_irqs (); - rte_cb_init_irqs (); -} diff --git a/arch/v850/kernel/rte_nb85e_cb.ld b/arch/v850/kernel/rte_nb85e_cb.ld deleted file mode 100644 index b672f484f085..000000000000 --- a/arch/v850/kernel/rte_nb85e_cb.ld +++ /dev/null @@ -1,22 +0,0 @@ -/* Linker script for the Midas labs RTE-NB85E-CB evaluation board - (CONFIG_RTE_CB_NB85E). */ - -MEMORY { - LOW : ORIGIN = 0x0, LENGTH = 0x00100000 - /* 1MB of SRAM This memory is mirrored 4 times. */ - SRAM : ORIGIN = SRAM_ADDR, LENGTH = SRAM_SIZE - /* 16MB of SDRAM. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -#ifdef CONFIG_RTE_CB_NB85E_KSRAM -# define KRAM SRAM -#else -# define KRAM SDRAM -#endif - -SECTIONS { - .intv : { INTV_CONTENTS } > LOW - .sram : { RAMK_KRAM_CONTENTS } > KRAM - .root : { ROOT_FS_CONTENTS } > SDRAM -} diff --git a/arch/v850/kernel/setup.c b/arch/v850/kernel/setup.c deleted file mode 100644 index 10335cecf7bd..000000000000 --- a/arch/v850/kernel/setup.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * arch/v850/kernel/setup.c -- Arch-dependent initialization functions - * - * Copyright (C) 2001,02,03,05,06 NEC Electronics Corporation - * Copyright (C) 2001,02,03,05,06 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include /* we don't have swap, but for nr_free_pages */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "mach.h" - -/* These symbols are all defined in the linker map to delineate various - statically allocated regions of memory. */ - -extern char _intv_start, _intv_end; -/* `kram' is only used if the kernel uses part of normal user RAM. */ -extern char _kram_start __attribute__ ((__weak__)); -extern char _kram_end __attribute__ ((__weak__)); -extern char _init_start, _init_end; -extern char _bootmap; -extern char _stext, _etext, _sdata, _edata, _sbss, _ebss; -/* Many platforms use an embedded root image. */ -extern char _root_fs_image_start __attribute__ ((__weak__)); -extern char _root_fs_image_end __attribute__ ((__weak__)); - - -char __initdata command_line[COMMAND_LINE_SIZE]; - -/* Memory not used by the kernel. */ -static unsigned long total_ram_pages; - -/* System RAM. */ -static unsigned long ram_start = 0, ram_len = 0; - - -#define ADDR_TO_PAGE_UP(x) ((((unsigned long)x) + PAGE_SIZE-1) >> PAGE_SHIFT) -#define ADDR_TO_PAGE(x) (((unsigned long)x) >> PAGE_SHIFT) -#define PAGE_TO_ADDR(x) (((unsigned long)x) << PAGE_SHIFT) - -static void init_mem_alloc (unsigned long ram_start, unsigned long ram_len); - -void set_mem_root (void *addr, size_t len, char *cmd_line); - - -void __init setup_arch (char **cmdline) -{ - /* Keep a copy of command line */ - *cmdline = command_line; - memcpy (boot_command_line, command_line, COMMAND_LINE_SIZE); - boot_command_line[COMMAND_LINE_SIZE - 1] = '\0'; - - console_verbose (); - - init_mm.start_code = (unsigned long) &_stext; - init_mm.end_code = (unsigned long) &_etext; - init_mm.end_data = (unsigned long) &_edata; - init_mm.brk = (unsigned long) &_kram_end; - - /* Find out what mem this machine has. */ - mach_get_physical_ram (&ram_start, &ram_len); - /* ... and tell the kernel about it. */ - init_mem_alloc (ram_start, ram_len); - - printk (KERN_INFO "CPU: %s\nPlatform: %s\n", - CPU_MODEL_LONG, PLATFORM_LONG); - - /* do machine-specific setups. */ - mach_setup (cmdline); - -#ifdef CONFIG_MTD - if (!ROOT_DEV && &_root_fs_image_end > &_root_fs_image_start) - set_mem_root (&_root_fs_image_start, - &_root_fs_image_end - &_root_fs_image_start, - *cmdline); -#endif -} - -void __init trap_init (void) -{ -} - -#ifdef CONFIG_MTD - -/* From drivers/mtd/devices/slram.c */ -#define SLRAM_BLK_SZ 0x4000 - -/* Set the root filesystem to be the given memory region. - Some parameter may be appended to CMD_LINE. */ -void set_mem_root (void *addr, size_t len, char *cmd_line) -{ - /* Some sort of idiocy in MTD means we must supply a length that's - a multiple of SLRAM_BLK_SZ. We just round up the real length, - as the file system shouldn't attempt to access anything beyond - the end of the image anyway. */ - len = (((len - 1) + SLRAM_BLK_SZ) / SLRAM_BLK_SZ) * SLRAM_BLK_SZ; - - /* The only way to pass info to the MTD slram driver is via - the command line. */ - if (*cmd_line) { - cmd_line += strlen (cmd_line); - *cmd_line++ = ' '; - } - sprintf (cmd_line, "slram=root,0x%x,+0x%x", (u32)addr, (u32)len); - - ROOT_DEV = MKDEV (MTD_BLOCK_MAJOR, 0); -} -#endif - - -static void irq_nop (unsigned irq) { } -static unsigned irq_zero (unsigned irq) { return 0; } - -static void nmi_end (unsigned irq) -{ - if (irq != IRQ_NMI (0)) { - printk (KERN_CRIT "NMI %d is unrecoverable; restarting...", - irq - IRQ_NMI (0)); - machine_restart (0); - } -} - -static struct hw_interrupt_type nmi_irq_type = { - .typename = "NMI", - .startup = irq_zero, /* startup */ - .shutdown = irq_nop, /* shutdown */ - .enable = irq_nop, /* enable */ - .disable = irq_nop, /* disable */ - .ack = irq_nop, /* ack */ - .end = nmi_end, /* end */ -}; - -void __init init_IRQ (void) -{ - init_irq_handlers (0, NUM_MACH_IRQS, 1, 0); - init_irq_handlers (IRQ_NMI (0), NUM_NMIS, 1, &nmi_irq_type); - mach_init_irqs (); -} - - -void __init mem_init (void) -{ - max_mapnr = MAP_NR (ram_start + ram_len); - - num_physpages = ADDR_TO_PAGE (ram_len); - - total_ram_pages = free_all_bootmem (); - - printk (KERN_INFO - "Memory: %luK/%luK available" - " (%luK kernel code, %luK data)\n", - PAGE_TO_ADDR (nr_free_pages()) / 1024, - ram_len / 1024, - ((unsigned long)&_etext - (unsigned long)&_stext) / 1024, - ((unsigned long)&_ebss - (unsigned long)&_sdata) / 1024); -} - -void free_initmem (void) -{ - unsigned long ram_end = ram_start + ram_len; - unsigned long start = PAGE_ALIGN ((unsigned long)(&_init_start)); - - if (start >= ram_start && start < ram_end) { - unsigned long addr; - unsigned long end = PAGE_ALIGN ((unsigned long)(&_init_end)); - - if (end > ram_end) - end = ram_end; - - printk("Freeing unused kernel memory: %ldK freed\n", - (end - start) / 1024); - - for (addr = start; addr < end; addr += PAGE_SIZE) { - struct page *page = virt_to_page (addr); - ClearPageReserved (page); - init_page_count (page); - __free_page (page); - total_ram_pages++; - } - } -} - - -/* Initialize the `bootmem allocator'. RAM_START and RAM_LEN identify - what RAM may be used. */ -static void __init -init_bootmem_alloc (unsigned long ram_start, unsigned long ram_len) -{ - /* The part of the kernel that's in the same managed RAM space - used for general allocation. */ - unsigned long kram_start = (unsigned long)&_kram_start; - unsigned long kram_end = (unsigned long)&_kram_end; - /* End of the managed RAM space. */ - unsigned long ram_end = ram_start + ram_len; - /* Address range of the interrupt vector table. */ - unsigned long intv_start = (unsigned long)&_intv_start; - unsigned long intv_end = (unsigned long)&_intv_end; - /* True if the interrupt vectors are in the managed RAM area. */ - int intv_in_ram = (intv_end > ram_start && intv_start < ram_end); - /* True if the interrupt vectors are inside the kernel's RAM. */ - int intv_in_kram = (intv_end > kram_start && intv_start < kram_end); - /* A pointer to an optional function that reserves platform-specific - memory regions. We declare the pointer `volatile' to avoid gcc - turning the call into a static call (the problem is that since - it's a weak symbol, a static call may end up trying to reference - the location 0x0, which is not always reachable). */ - void (*volatile mrb) (void) = mach_reserve_bootmem; - /* The bootmem allocator's allocation bitmap. */ - unsigned long bootmap = (unsigned long)&_bootmap; - unsigned long bootmap_len; - - /* Round bootmap location up to next page. */ - bootmap = PAGE_TO_ADDR (ADDR_TO_PAGE_UP (bootmap)); - - /* Initialize bootmem allocator. */ - bootmap_len = init_bootmem_node (NODE_DATA (0), - ADDR_TO_PAGE (bootmap), - ADDR_TO_PAGE (PAGE_OFFSET), - ADDR_TO_PAGE (ram_end)); - - /* Now make the RAM actually allocatable (it starts out `reserved'). */ - free_bootmem (ram_start, ram_len); - - if (kram_end > kram_start) - /* Reserve the RAM part of the kernel's address space, so it - doesn't get allocated. */ - reserve_bootmem(kram_start, kram_end - kram_start, - BOOTMEM_DEFAULT); - - if (intv_in_ram && !intv_in_kram) - /* Reserve the interrupt vector space. */ - reserve_bootmem(intv_start, intv_end - intv_start, - BOOTMEM_DEFAULT); - - if (bootmap >= ram_start && bootmap < ram_end) - /* Reserve the bootmap space. */ - reserve_bootmem(bootmap, bootmap_len, - BOOTMEM_DEFAULT); - - /* Reserve the memory used by the root filesystem image if it's - in RAM. */ - if (&_root_fs_image_end > &_root_fs_image_start - && (unsigned long)&_root_fs_image_start >= ram_start - && (unsigned long)&_root_fs_image_start < ram_end) - reserve_bootmem ((unsigned long)&_root_fs_image_start, - &_root_fs_image_end - &_root_fs_image_start, - BOOTMEM_DEFAULT); - - /* Let the platform-dependent code reserve some too. */ - if (mrb) - (*mrb) (); -} - -/* Tell the kernel about what RAM it may use for memory allocation. */ -static void __init -init_mem_alloc (unsigned long ram_start, unsigned long ram_len) -{ - unsigned i; - unsigned long zones_size[MAX_NR_ZONES]; - - init_bootmem_alloc (ram_start, ram_len); - - for (i = 0; i < MAX_NR_ZONES; i++) - zones_size[i] = 0; - - /* We stuff all the memory into one area, which includes the - initial gap from PAGE_OFFSET to ram_start. */ - zones_size[ZONE_DMA] - = ADDR_TO_PAGE (ram_len + (ram_start - PAGE_OFFSET)); - - /* The allocator is very picky about the address of the first - allocatable page -- it must be at least as aligned as the - maximum allocation -- so try to detect cases where it will get - confused and signal them at compile time (this is a common - problem when porting to a new platform with ). There is a - similar runtime check in free_area_init_core. */ -#if ((PAGE_OFFSET >> PAGE_SHIFT) & ((1UL << (MAX_ORDER - 1)) - 1)) -#error MAX_ORDER is too large for given PAGE_OFFSET (use CONFIG_FORCE_MAX_ZONEORDER to change it) -#endif - NODE_DATA(0)->node_mem_map = NULL; - free_area_init_node(0, zones_size, ADDR_TO_PAGE (PAGE_OFFSET), 0); -} - - - -/* Taken from m68knommu */ -void show_mem(void) -{ - unsigned long i; - int free = 0, total = 0, reserved = 0, shared = 0; - int cached = 0; - - printk(KERN_INFO "\nMem-info:\n"); - show_free_areas(); - i = max_mapnr; - while (i-- > 0) { - total++; - if (PageReserved(mem_map+i)) - reserved++; - else if (PageSwapCache(mem_map+i)) - cached++; - else if (!page_count(mem_map+i)) - free++; - else - shared += page_count(mem_map+i) - 1; - } - printk(KERN_INFO "%d pages of RAM\n",total); - printk(KERN_INFO "%d free pages\n",free); - printk(KERN_INFO "%d reserved pages\n",reserved); - printk(KERN_INFO "%d pages shared\n",shared); - printk(KERN_INFO "%d pages swap cached\n",cached); -} diff --git a/arch/v850/kernel/signal.c b/arch/v850/kernel/signal.c deleted file mode 100644 index bf166e7e762c..000000000000 --- a/arch/v850/kernel/signal.c +++ /dev/null @@ -1,523 +0,0 @@ -/* - * arch/v850/kernel/signal.c -- Signal handling - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * Copyright (C) 1999,2000,2002 Niibe Yutaka & Kaz Kojima - * Copyright (C) 1991,1992 Linus Torvalds - * - * 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 for more details. - * - * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson - * - * This file was derived from the sh version, arch/sh/kernel/signal.c - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define DEBUG_SIG 0 - -#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) - -asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); - -/* - * Atomically swap in the new signal mask, and wait for a signal. - */ -asmlinkage int -sys_sigsuspend(old_sigset_t mask, struct pt_regs *regs) -{ - sigset_t saveset; - - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - siginitset(¤t->blocked, mask); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->gpr[GPR_RVAL] = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(regs, &saveset)) - return -EINTR; - } -} - -asmlinkage int -sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, - struct pt_regs *regs) -{ - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->gpr[GPR_RVAL] = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(regs, &saveset)) - return -EINTR; - } -} - -asmlinkage int -sys_sigaction(int sig, const struct old_sigaction *act, - struct old_sigaction *oact) -{ - struct k_sigaction new_ka, old_ka; - int ret; - - if (act) { - old_sigset_t mask; - if (!access_ok(VERIFY_READ, act, sizeof(*act)) || - __get_user(new_ka.sa.sa_handler, &act->sa_handler) || - __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) - return -EFAULT; - __get_user(new_ka.sa.sa_flags, &act->sa_flags); - __get_user(mask, &act->sa_mask); - siginitset(&new_ka.sa.sa_mask, mask); - } - - ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); - - if (!ret && oact) { - if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || - __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || - __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) - return -EFAULT; - __put_user(old_ka.sa.sa_flags, &oact->sa_flags); - __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); - } - - return ret; -} - -asmlinkage int -sys_sigaltstack(const stack_t *uss, stack_t *uoss, - struct pt_regs *regs) -{ - return do_sigaltstack(uss, uoss, regs->gpr[GPR_SP]); -} - - -/* - * Do a signal return; undo the signal stack. - */ - -struct sigframe -{ - struct sigcontext sc; - unsigned long extramask[_NSIG_WORDS-1]; - unsigned long tramp[2]; /* signal trampoline */ -}; - -struct rt_sigframe -{ - struct siginfo info; - struct ucontext uc; - unsigned long tramp[2]; /* signal trampoline */ -}; - -static int -restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, int *rval_p) -{ - unsigned int err = 0; - -#define COPY(x) err |= __get_user(regs->x, &sc->regs.x) - COPY(gpr[0]); COPY(gpr[1]); COPY(gpr[2]); COPY(gpr[3]); - COPY(gpr[4]); COPY(gpr[5]); COPY(gpr[6]); COPY(gpr[7]); - COPY(gpr[8]); COPY(gpr[9]); COPY(gpr[10]); COPY(gpr[11]); - COPY(gpr[12]); COPY(gpr[13]); COPY(gpr[14]); COPY(gpr[15]); - COPY(gpr[16]); COPY(gpr[17]); COPY(gpr[18]); COPY(gpr[19]); - COPY(gpr[20]); COPY(gpr[21]); COPY(gpr[22]); COPY(gpr[23]); - COPY(gpr[24]); COPY(gpr[25]); COPY(gpr[26]); COPY(gpr[27]); - COPY(gpr[28]); COPY(gpr[29]); COPY(gpr[30]); COPY(gpr[31]); - COPY(pc); COPY(psw); - COPY(ctpc); COPY(ctpsw); COPY(ctbp); -#undef COPY - - return err; -} - -asmlinkage int sys_sigreturn(struct pt_regs *regs) -{ - struct sigframe *frame = (struct sigframe *)regs->gpr[GPR_SP]; - sigset_t set; - int rval; - - if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) - goto badframe; - - if (__get_user(set.sig[0], &frame->sc.oldmask) - || (_NSIG_WORDS > 1 - && __copy_from_user(&set.sig[1], &frame->extramask, - sizeof(frame->extramask)))) - goto badframe; - - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - if (restore_sigcontext(regs, &frame->sc, &rval)) - goto badframe; - return rval; - -badframe: - force_sig(SIGSEGV, current); - return 0; -} - -asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) -{ - struct rt_sigframe *frame = (struct rt_sigframe *)regs->gpr[GPR_SP]; - sigset_t set; - stack_t st; - int rval; - - if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) - goto badframe; - - if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) - goto badframe; - - sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &rval)) - goto badframe; - - if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) - goto badframe; - /* It is more difficult to avoid calling this function than to - call it and ignore errors. */ - do_sigaltstack(&st, NULL, regs->gpr[GPR_SP]); - - return rval; - -badframe: - force_sig(SIGSEGV, current); - return 0; -} - -/* - * Set up a signal frame. - */ - -static int -setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, - unsigned long mask) -{ - int err = 0; - -#define COPY(x) err |= __put_user(regs->x, &sc->regs.x) - COPY(gpr[0]); COPY(gpr[1]); COPY(gpr[2]); COPY(gpr[3]); - COPY(gpr[4]); COPY(gpr[5]); COPY(gpr[6]); COPY(gpr[7]); - COPY(gpr[8]); COPY(gpr[9]); COPY(gpr[10]); COPY(gpr[11]); - COPY(gpr[12]); COPY(gpr[13]); COPY(gpr[14]); COPY(gpr[15]); - COPY(gpr[16]); COPY(gpr[17]); COPY(gpr[18]); COPY(gpr[19]); - COPY(gpr[20]); COPY(gpr[21]); COPY(gpr[22]); COPY(gpr[23]); - COPY(gpr[24]); COPY(gpr[25]); COPY(gpr[26]); COPY(gpr[27]); - COPY(gpr[28]); COPY(gpr[29]); COPY(gpr[30]); COPY(gpr[31]); - COPY(pc); COPY(psw); - COPY(ctpc); COPY(ctpsw); COPY(ctbp); -#undef COPY - - err |= __put_user(mask, &sc->oldmask); - - return err; -} - -/* - * Determine which stack to use.. - */ -static inline void * -get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) -{ - /* Default to using normal stack */ - unsigned long sp = regs->gpr[GPR_SP]; - - if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! sas_ss_flags(sp)) - sp = current->sas_ss_sp + current->sas_ss_size; - - return (void *)((sp - frame_size) & -8UL); -} - -static void setup_frame(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs *regs) -{ - struct sigframe *frame; - int err = 0; - int signal; - - frame = get_sigframe(ka, regs, sizeof(*frame)); - - if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; - - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - - err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); - - if (_NSIG_WORDS > 1) { - err |= __copy_to_user(frame->extramask, &set->sig[1], - sizeof(frame->extramask)); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa.sa_flags & SA_RESTORER) { - regs->gpr[GPR_LP] = (unsigned long) ka->sa.sa_restorer; - } else { - /* Note, these encodings are _little endian_! */ - - /* addi __NR_sigreturn, r0, r12 */ - err |= __put_user(0x6600 | (__NR_sigreturn << 16), - frame->tramp + 0); - /* trap 0 */ - err |= __put_user(0x010007e0, - frame->tramp + 1); - - regs->gpr[GPR_LP] = (unsigned long)frame->tramp; - - flush_cache_sigtramp (regs->gpr[GPR_LP]); - } - - if (err) - goto give_sigsegv; - - /* Set up registers for signal handler. */ - regs->pc = (v850_reg_t) ka->sa.sa_handler; - regs->gpr[GPR_SP] = (v850_reg_t)frame; - /* Signal handler args: */ - regs->gpr[GPR_ARG0] = signal; /* arg 0: signum */ - regs->gpr[GPR_ARG1] = (v850_reg_t)&frame->sc;/* arg 1: sigcontext */ - - set_fs(USER_DS); - -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%08lx ra=%08lx\n", - current->comm, current->pid, frame, regs->pc, ); -#endif - - return; - -give_sigsegv: - force_sigsegv(sig, current); -} - -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) -{ - struct rt_sigframe *frame; - int err = 0; - int signal; - - frame = get_sigframe(ka, regs, sizeof(*frame)); - - if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; - - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - - err |= copy_siginfo_to_user(&frame->info, info); - - /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); - err |= __put_user((void *)current->sas_ss_sp, - &frame->uc.uc_stack.ss_sp); - err |= __put_user(sas_ss_flags(regs->gpr[GPR_SP]), - &frame->uc.uc_stack.ss_flags); - err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); - err |= setup_sigcontext(&frame->uc.uc_mcontext, - regs, set->sig[0]); - err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa.sa_flags & SA_RESTORER) { - regs->gpr[GPR_LP] = (unsigned long) ka->sa.sa_restorer; - } else { - /* Note, these encodings are _little endian_! */ - - /* addi __NR_sigreturn, r0, r12 */ - err |= __put_user(0x6600 | (__NR_sigreturn << 16), - frame->tramp + 0); - /* trap 0 */ - err |= __put_user(0x010007e0, - frame->tramp + 1); - - regs->gpr[GPR_LP] = (unsigned long)frame->tramp; - - flush_cache_sigtramp (regs->gpr[GPR_LP]); - } - - if (err) - goto give_sigsegv; - - /* Set up registers for signal handler. */ - regs->pc = (v850_reg_t) ka->sa.sa_handler; - regs->gpr[GPR_SP] = (v850_reg_t)frame; - /* Signal handler args: */ - regs->gpr[GPR_ARG0] = signal; /* arg 0: signum */ - regs->gpr[GPR_ARG1] = (v850_reg_t)&frame->info; /* arg 1: siginfo */ - regs->gpr[GPR_ARG2] = (v850_reg_t)&frame->uc; /* arg 2: ucontext */ - - set_fs(USER_DS); - -#if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", - current->comm, current->pid, frame, regs->pc, regs->pr); -#endif - - return; - -give_sigsegv: - force_sigsegv(sig, current); -} - -/* - * OK, we're invoking a handler - */ - -static void -handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, - sigset_t *oldset, struct pt_regs * regs) -{ - /* Are we from a system call? */ - if (PT_REGS_SYSCALL (regs)) { - /* If so, check system call restarting.. */ - switch (regs->gpr[GPR_RVAL]) { - case -ERESTART_RESTARTBLOCK: - current_thread_info()->restart_block.fn = - do_no_restart_syscall; - /* fall through */ - case -ERESTARTNOHAND: - regs->gpr[GPR_RVAL] = -EINTR; - break; - - case -ERESTARTSYS: - if (!(ka->sa.sa_flags & SA_RESTART)) { - regs->gpr[GPR_RVAL] = -EINTR; - break; - } - /* fallthrough */ - case -ERESTARTNOINTR: - regs->gpr[12] = PT_REGS_SYSCALL (regs); - regs->pc -= 4; /* Size of `trap 0' insn. */ - } - - PT_REGS_SET_SYSCALL (regs, 0); - } - - /* Set up the stack frame */ - if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); - else - setup_frame(sig, ka, oldset, regs); - - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); -} - -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - * - * Note that we go through the signals twice: once to check the signals that - * the kernel can handle, and then we build all the user-level signal handling - * stack-frames in one go after that. - */ -int do_signal(struct pt_regs *regs, sigset_t *oldset) -{ - siginfo_t info; - int signr; - struct k_sigaction ka; - - /* - * We want the common case to go fast, which - * is why we may in certain cases get here from - * kernel mode. Just return without doing anything - * if so. - */ - if (!user_mode(regs)) - return 1; - - if (!oldset) - oldset = ¤t->blocked; - - signr = get_signal_to_deliver(&info, &ka, regs, NULL); - if (signr > 0) { - /* Whee! Actually deliver the signal. */ - handle_signal(signr, &info, &ka, oldset, regs); - return 1; - } - - /* Did we come from a system call? */ - if (PT_REGS_SYSCALL (regs)) { - int rval = (int)regs->gpr[GPR_RVAL]; - /* Restart the system call - no handlers present */ - if (rval == -ERESTARTNOHAND - || rval == -ERESTARTSYS - || rval == -ERESTARTNOINTR) - { - regs->gpr[12] = PT_REGS_SYSCALL (regs); - regs->pc -= 4; /* Size of `trap 0' insn. */ - } - else if (rval == -ERESTART_RESTARTBLOCK) { - regs->gpr[12] = __NR_restart_syscall; - regs->pc -= 4; /* Size of `trap 0' insn. */ - } - } - return 0; -} diff --git a/arch/v850/kernel/sim.c b/arch/v850/kernel/sim.c deleted file mode 100644 index 467b4aa0acdd..000000000000 --- a/arch/v850/kernel/sim.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * arch/v850/kernel/sim.c -- Machine-specific stuff for GDB v850e simulator - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mach.h" - -/* The name of a file containing the root filesystem. */ -#define ROOT_FS "rootfs.image" - -extern void simcons_setup (void); -extern void simcons_poll_ttys (void); -extern void set_mem_root (void *addr, size_t len, char *cmd_line); - -static int read_file (const char *name, - unsigned long *addr, unsigned long *len, - const char **err); - -void __init mach_setup (char **cmdline) -{ - const char *err; - unsigned long root_dev_addr, root_dev_len; - - simcons_setup (); - - printk (KERN_INFO "Reading root filesystem: %s", ROOT_FS); - - if (read_file (ROOT_FS, &root_dev_addr, &root_dev_len, &err)) { - printk (" (size %luK)\n", root_dev_len / 1024); - set_mem_root ((void *)root_dev_addr, (size_t)root_dev_len, - *cmdline); - } else - printk ("...%s failed!\n", err); -} - -void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len) -{ - *ram_start = RAM_ADDR; - *ram_len = RAM_SIZE; -} - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* ...do magic timer initialization?... */ - mach_tick = simcons_poll_ttys; - setup_irq (0, timer_action); -} - - -static void irq_nop (unsigned irq) { } -static unsigned irq_zero (unsigned irq) { return 0; } - -static struct hw_interrupt_type sim_irq_type = { - .typename = "IRQ", - .startup = irq_zero, /* startup */ - .shutdown = irq_nop, /* shutdown */ - .enable = irq_nop, /* enable */ - .disable = irq_nop, /* disable */ - .ack = irq_nop, /* ack */ - .end = irq_nop, /* end */ -}; - -void __init mach_init_irqs (void) -{ - init_irq_handlers (0, NUM_MACH_IRQS, 1, &sim_irq_type); -} - - -void mach_gettimeofday (struct timespec *tv) -{ - long timeval[2], timezone[2]; - int rval = V850_SIM_SYSCALL (gettimeofday, timeval, timezone); - if (rval == 0) { - tv->tv_sec = timeval[0]; - tv->tv_nsec = timeval[1] * 1000; - } -} - -void machine_restart (char *__unused) -{ - V850_SIM_SYSCALL (write, 1, "RESTART\n", 8); - V850_SIM_SYSCALL (exit, 0); -} - -void machine_halt (void) -{ - V850_SIM_SYSCALL (write, 1, "HALT\n", 5); - V850_SIM_SYSCALL (exit, 0); -} - -void machine_power_off (void) -{ - V850_SIM_SYSCALL (write, 1, "POWER OFF\n", 10); - V850_SIM_SYSCALL (exit, 0); -} - - -/* Load data from a file called NAME into ram. The address and length - of the data image are returned in ADDR and LEN. */ -static int __init -read_file (const char *name, - unsigned long *addr, unsigned long *len, - const char **err) -{ - int rval, fd; - unsigned long cur, left; - /* Note this is not a normal stat buffer, it's an ad-hoc - structure defined by the simulator. */ - unsigned long stat_buf[10]; - - /* Stat the file to find out the length. */ - rval = V850_SIM_SYSCALL (stat, name, stat_buf); - if (rval < 0) { - if (err) *err = "stat"; - return 0; - } - *len = stat_buf[4]; - - /* Open the file; `0' is O_RDONLY. */ - fd = V850_SIM_SYSCALL (open, name, 0); - if (fd < 0) { - if (err) *err = "open"; - return 0; - } - - *addr = (unsigned long)alloc_bootmem(*len); - if (! *addr) { - V850_SIM_SYSCALL (close, fd); - if (err) *err = "alloc_bootmem"; - return 0; - } - - cur = *addr; - left = *len; - while (left > 0) { - int chunk = V850_SIM_SYSCALL (read, fd, cur, left); - if (chunk <= 0) - break; - cur += chunk; - left -= chunk; - } - V850_SIM_SYSCALL (close, fd); - if (left > 0) { - /* Some read failed. */ - free_bootmem (*addr, *len); - if (err) *err = "read"; - return 0; - } - - return 1; -} diff --git a/arch/v850/kernel/sim.ld b/arch/v850/kernel/sim.ld deleted file mode 100644 index 101885f3c9f0..000000000000 --- a/arch/v850/kernel/sim.ld +++ /dev/null @@ -1,13 +0,0 @@ -/* Linker script for the gdb v850e simulator (CONFIG_V850E_SIM). */ - -MEMORY { - /* Interrupt vectors. */ - INTV : ORIGIN = 0x0, LENGTH = 0xe0 - /* Main RAM. */ - RAM : ORIGIN = RAM_ADDR, LENGTH = RAM_SIZE -} - -SECTIONS { - .intv : { INTV_CONTENTS } > INTV - .ram : { RAMK_KRAM_CONTENTS } > RAM -} diff --git a/arch/v850/kernel/sim85e2.c b/arch/v850/kernel/sim85e2.c deleted file mode 100644 index 566dde5e6070..000000000000 --- a/arch/v850/kernel/sim85e2.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * arch/v850/kernel/sim85e2.c -- Machine-specific stuff for - * V850E2 RTL simulator - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "mach.h" - - -/* There are 4 possible areas we can use: - - IRAM (1MB) is fast for instruction fetches, but slow for data - DRAM (1020KB) is fast for data, but slow for instructions - ERAM is cached, so should be fast for both insns and data - SDRAM is external DRAM, similar to ERAM -*/ - -#define INIT_MEMC_FOR_SDRAM -#define USE_SDRAM_AREA -#define KERNEL_IN_SDRAM_AREA - -#define DCACHE_MODE V850E2_CACHE_BTSC_DCM_WT -/*#define DCACHE_MODE V850E2_CACHE_BTSC_DCM_WB_ALLOC*/ - -#ifdef USE_SDRAM_AREA -#define RAM_START SDRAM_ADDR -#define RAM_END (SDRAM_ADDR + SDRAM_SIZE) -#else -/* When we use DRAM, we need to account for the fact that the end of it is - used for R0_RAM. */ -#define RAM_START DRAM_ADDR -#define RAM_END R0_RAM_ADDR -#endif - - -extern void memcons_setup (void); - - -#ifdef KERNEL_IN_SDRAM_AREA -#define EARLY_INIT_SECTION_ATTR __attribute__ ((section (".early.text"))) -#else -#define EARLY_INIT_SECTION_ATTR __init -#endif - -void EARLY_INIT_SECTION_ATTR mach_early_init (void) -{ - /* The sim85e2 simulator tracks `undefined' values, so to make - debugging easier, we begin by zeroing out all otherwise - undefined registers. This is not strictly necessary. - - The registers we zero are: - Every GPR except: - stack-pointer (r3) - task-pointer (r16) - our return addr (r31) - Every system register (SPR) that we know about except for - the PSW (SPR 5), which we zero except for the - disable-interrupts bit. - */ - - /* GPRs */ - asm volatile (" mov r0, r1 ; mov r0, r2 "); - asm volatile ("mov r0, r4 ; mov r0, r5 ; mov r0, r6 ; mov r0, r7 "); - asm volatile ("mov r0, r8 ; mov r0, r9 ; mov r0, r10; mov r0, r11"); - asm volatile ("mov r0, r12; mov r0, r13; mov r0, r14; mov r0, r15"); - asm volatile (" mov r0, r17; mov r0, r18; mov r0, r19"); - asm volatile ("mov r0, r20; mov r0, r21; mov r0, r22; mov r0, r23"); - asm volatile ("mov r0, r24; mov r0, r25; mov r0, r26; mov r0, r27"); - asm volatile ("mov r0, r28; mov r0, r29; mov r0, r30"); - - /* SPRs */ - asm volatile ("ldsr r0, 0; ldsr r0, 1; ldsr r0, 2; ldsr r0, 3"); - asm volatile ("ldsr r0, 4"); - asm volatile ("addi 0x20, r0, r1; ldsr r1, 5"); /* PSW */ - asm volatile ("ldsr r0, 16; ldsr r0, 17; ldsr r0, 18; ldsr r0, 19"); - asm volatile ("ldsr r0, 20"); - - -#ifdef INIT_MEMC_FOR_SDRAM - /* Settings for SDRAM controller. */ - V850E2_VSWC = 0x0042; - V850E2_BSC = 0x9286; - V850E2_BCT(0) = 0xb000; /* was: 0 */ - V850E2_BCT(1) = 0x000b; - V850E2_ASC = 0; - V850E2_LBS = 0xa9aa; /* was: 0xaaaa */ - V850E2_LBC(0) = 0; - V850E2_LBC(1) = 0; /* was: 0x3 */ - V850E2_BCC = 0; - V850E2_RFS(4) = 0x800a; /* was: 0xf109 */ - V850E2_SCR(4) = 0x2091; /* was: 0x20a1 */ - V850E2_RFS(3) = 0x800c; - V850E2_SCR(3) = 0x20a1; - V850E2_DWC(0) = 0; - V850E2_DWC(1) = 0; -#endif - -#if 0 -#ifdef CONFIG_V850E2_SIM85E2S - /* Turn on the caches. */ - V850E2_CACHE_BTSC = V850E2_CACHE_BTSC_ICM | DCACHE_MODE; - V850E2_BHC = 0x1010; -#elif CONFIG_V850E2_SIM85E2C - V850E2_CACHE_BTSC |= (V850E2_CACHE_BTSC_ICM | V850E2_CACHE_BTSC_DCM0); - V850E2_BUSM_BHC = 0xFFFF; -#endif -#else - V850E2_BHC = 0; -#endif - - /* Don't stop the simulator at `halt' instructions. */ - SIM85E2_NOTHAL = 1; - - /* Ensure that the simulator halts on a panic, instead of going - into an infinite loop inside the panic function. */ - panic_timeout = -1; -} - -void __init mach_setup (char **cmdline) -{ - memcons_setup (); -} - -void mach_get_physical_ram (unsigned long *ram_start, unsigned long *ram_len) -{ - *ram_start = RAM_START; - *ram_len = RAM_END - RAM_START; -} - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* The simulator actually cycles through all interrupts - periodically. We just pay attention to IRQ0, which gives us - 1/64 the rate of the periodic interrupts. */ - setup_irq (0, timer_action); -} - -void mach_gettimeofday (struct timespec *tv) -{ - tv->tv_sec = 0; - tv->tv_nsec = 0; -} - -/* Interrupts */ - -struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_MACH_IRQS, 1, 7 }, - { 0 } -}; -struct hw_interrupt_type hw_itypes[1]; - -/* Initialize interrupts. */ -void __init mach_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - - -void machine_halt (void) __attribute__ ((noreturn)); -void machine_halt (void) -{ - SIM85E2_SIMFIN = 0; /* Halt immediately. */ - for (;;) {} -} - -void machine_restart (char *__unused) -{ - machine_halt (); -} - -void machine_power_off (void) -{ - machine_halt (); -} - diff --git a/arch/v850/kernel/sim85e2.ld b/arch/v850/kernel/sim85e2.ld deleted file mode 100644 index 7470fd2ffb5b..000000000000 --- a/arch/v850/kernel/sim85e2.ld +++ /dev/null @@ -1,36 +0,0 @@ -/* Linker script for the sim85e2c simulator, which is a verilog simulation of - the V850E2 NA85E2C cpu core (CONFIG_V850E2_SIM85E2C). */ - -MEMORY { - /* 1MB of `instruction RAM', starting at 0. - Instruction fetches are much faster from IRAM than from DRAM. */ - IRAM : ORIGIN = IRAM_ADDR, LENGTH = IRAM_SIZE - - /* 1MB of `data RAM', below and contiguous with the I/O space. - Data fetches are much faster from DRAM than from IRAM. */ - DRAM : ORIGIN = DRAM_ADDR, LENGTH = DRAM_SIZE - - /* `external ram' (CS1 area), comes after IRAM. */ - ERAM : ORIGIN = ERAM_ADDR, LENGTH = ERAM_SIZE - - /* Dynamic RAM; uses memory controller. */ - SDRAM : ORIGIN = SDRAM_ADDR, LENGTH = SDRAM_SIZE -} - -SECTIONS { - .iram : { - INTV_CONTENTS - *arch/v850/kernel/head.o - *(.early.text) - } > IRAM - .dram : { - _memcons_output = . ; - . = . + 0x8000 ; - _memcons_output_end = . ; - } > DRAM - .sdram : { - /* We stick console output into a buffer here. */ - RAMK_KRAM_CONTENTS - ROOT_FS_CONTENTS - } > SDRAM -} diff --git a/arch/v850/kernel/simcons.c b/arch/v850/kernel/simcons.c deleted file mode 100644 index 9973596ae304..000000000000 --- a/arch/v850/kernel/simcons.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * arch/v850/kernel/simcons.c -- Console I/O for GDB v850e simulator - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - -/* Low-level console. */ - -static void simcons_write (struct console *co, const char *buf, unsigned len) -{ - V850_SIM_SYSCALL (write, 1, buf, len); -} - -static int simcons_read (struct console *co, char *buf, unsigned len) -{ - return V850_SIM_SYSCALL (read, 0, buf, len); -} - -static struct tty_driver *tty_driver; -static struct tty_driver *simcons_device (struct console *c, int *index) -{ - *index = c->index; - return tty_driver; -} - -static struct console simcons = -{ - .name = "simcons", - .write = simcons_write, - .read = simcons_read, - .device = simcons_device, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -/* Higher level TTY interface. */ - -int simcons_tty_open (struct tty_struct *tty, struct file *filp) -{ - return 0; -} - -int simcons_tty_write (struct tty_struct *tty, - const unsigned char *buf, int count) -{ - return V850_SIM_SYSCALL (write, 1, buf, count); -} - -int simcons_tty_write_room (struct tty_struct *tty) -{ - /* Completely arbitrary. */ - return 0x100000; -} - -int simcons_tty_chars_in_buffer (struct tty_struct *tty) -{ - /* We have no buffer. */ - return 0; -} - -static const struct tty_operations ops = { - .open = simcons_tty_open, - .write = simcons_tty_write, - .write_room = simcons_tty_write_room, - .chars_in_buffer = simcons_tty_chars_in_buffer, -}; - -int __init simcons_tty_init (void) -{ - struct tty_driver *driver = alloc_tty_driver(1); - int err; - if (!driver) - return -ENOMEM; - driver->name = "simcons"; - driver->major = TTY_MAJOR; - driver->minor_start = 64; - driver->type = TTY_DRIVER_TYPE_SYSCONS; - driver->init_termios = tty_std_termios; - tty_set_operations(driver, &ops); - err = tty_register_driver(driver); - if (err) { - put_tty_driver(driver); - return err; - } - tty_driver = driver; - return 0; -} -/* We use `late_initcall' instead of just `__initcall' as a workaround for - the fact that (1) simcons_tty_init can't be called before tty_init, - (2) tty_init is called via `module_init', (3) if statically linked, - module_init == device_init, and (4) there's no ordering of init lists. - We can do this easily because simcons is always statically linked, but - other tty drivers that depend on tty_init and which must use - `module_init' to declare their init routines are likely to be broken. */ -late_initcall(simcons_tty_init); - -/* Poll for input on the console, and if there's any, deliver it to the - tty driver. */ -void simcons_poll_tty (struct tty_struct *tty) -{ - char buf[32]; /* Not the nicest way to do it but I need it correct first */ - int flip = 0, send_break = 0; - struct pollfd pfd; - pfd.fd = 0; - pfd.events = POLLIN; - - if (V850_SIM_SYSCALL (poll, &pfd, 1, 0) > 0) { - if (pfd.revents & POLLIN) { - /* Real block hardware knows the transfer size before - transfer so the new tty buffering doesn't try to handle - this rather weird simulator specific case well */ - int rd = V850_SIM_SYSCALL (read, 0, buf, 32); - if (rd > 0) { - tty_insert_flip_string(tty, buf, rd); - flip = 1; - } else - send_break = 1; - } else if (pfd.revents & POLLERR) - send_break = 1; - } - - if (send_break) { - tty_insert_flip_char (tty, 0, TTY_BREAK); - flip = 1; - } - - if (flip) - tty_schedule_flip (tty); -} - -void simcons_poll_ttys (void) -{ - if (tty_driver && tty_driver->ttys[0]) - simcons_poll_tty (tty_driver->ttys[0]); -} - -void simcons_setup (void) -{ - V850_SIM_SYSCALL (make_raw, 0); - register_console (&simcons); - printk (KERN_INFO "Console: GDB V850E simulator stdio\n"); -} diff --git a/arch/v850/kernel/syscalls.c b/arch/v850/kernel/syscalls.c deleted file mode 100644 index 1a83daf8e24f..000000000000 --- a/arch/v850/kernel/syscalls.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * arch/v850/kernel/syscalls.c -- Various system-call definitions not - * defined in machine-independent code - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * This file was derived the ppc version, arch/ppc/kernel/syscalls.c - * ... which was derived from "arch/i386/kernel/sys_i386.c" by Gary Thomas; - * modified by Cort Dougan (cort@cs.nmt.edu) - * and Paul Mackerras (paulus@cs.anu.edu.au). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* - * sys_ipc() is the de-multiplexer for the SysV IPC calls.. - * - * This is really horribly ugly. - */ -int -sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth) -{ - int version, ret; - - version = call >> 16; /* hack for backward compatibility */ - call &= 0xffff; - - ret = -EINVAL; - switch (call) { - case SEMOP: - ret = sys_semop (first, (struct sembuf *)ptr, second); - break; - case SEMGET: - ret = sys_semget (first, second, third); - break; - case SEMCTL: - { - union semun fourth; - - if (!ptr) - break; - if ((ret = access_ok(VERIFY_READ, ptr, sizeof(long)) ? 0 : -EFAULT) - || (ret = get_user(fourth.__pad, (void **)ptr))) - break; - ret = sys_semctl (first, second, third, fourth); - break; - } - case MSGSND: - ret = sys_msgsnd (first, (struct msgbuf *) ptr, second, third); - break; - case MSGRCV: - switch (version) { - case 0: { - struct ipc_kludge tmp; - - if (!ptr) - break; - if ((ret = access_ok(VERIFY_READ, ptr, sizeof(tmp)) ? 0 : -EFAULT) - || (ret = copy_from_user(&tmp, - (struct ipc_kludge *) ptr, - sizeof (tmp)))) - break; - ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, - third); - break; - } - default: - ret = sys_msgrcv (first, (struct msgbuf *) ptr, - second, fifth, third); - break; - } - break; - case MSGGET: - ret = sys_msgget ((key_t) first, second); - break; - case MSGCTL: - ret = sys_msgctl (first, second, (struct msqid_ds *) ptr); - break; - case SHMAT: - switch (version) { - default: { - ulong raddr; - - if ((ret = access_ok(VERIFY_WRITE, (ulong*) third, - sizeof(ulong)) ? 0 : -EFAULT)) - break; - ret = do_shmat (first, (char *) ptr, second, &raddr); - if (ret) - break; - ret = put_user (raddr, (ulong *) third); - break; - } - case 1: /* iBCS2 emulator entry point */ - if (!segment_eq(get_fs(), get_ds())) - break; - ret = do_shmat (first, (char *) ptr, second, - (ulong *) third); - break; - } - break; - case SHMDT: - ret = sys_shmdt ((char *)ptr); - break; - case SHMGET: - ret = sys_shmget (first, second, third); - break; - case SHMCTL: - ret = sys_shmctl (first, second, (struct shmid_ds *) ptr); - break; - } - - return ret; -} - -static inline unsigned long -do_mmap2 (unsigned long addr, size_t len, - unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long pgoff) -{ - struct file * file = NULL; - int ret = -EBADF; - - flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - if (! (flags & MAP_ANONYMOUS)) { - if (!(file = fget (fd))) - goto out; - } - - down_write (¤t->mm->mmap_sem); - ret = do_mmap_pgoff (file, addr, len, prot, flags, pgoff); - up_write (¤t->mm->mmap_sem); - if (file) - fput (file); -out: - return ret; -} - -unsigned long sys_mmap2 (unsigned long addr, size_t len, - unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long pgoff) -{ - return do_mmap2 (addr, len, prot, flags, fd, pgoff); -} - -unsigned long sys_mmap (unsigned long addr, size_t len, - unsigned long prot, unsigned long flags, - unsigned long fd, off_t offset) -{ - int err = -EINVAL; - - if (offset & ~PAGE_MASK) - goto out; - - err = do_mmap2 (addr, len, prot, flags, fd, offset >> PAGE_SHIFT); -out: - return err; -} - -/* - * Do a system call from kernel instead of calling sys_execve so we - * end up with proper pt_regs. - */ -int kernel_execve(const char *filename, char *const argv[], char *const envp[]) -{ - register char *__a __asm__ ("r6") = filename; - register void *__b __asm__ ("r7") = argv; - register void *__c __asm__ ("r8") = envp; - register unsigned long __syscall __asm__ ("r12") = __NR_execve; - register unsigned long __ret __asm__ ("r10"); - __asm__ __volatile__ ("trap 0" - : "=r" (__ret), "=r" (__syscall) - : "1" (__syscall), "r" (__a), "r" (__b), "r" (__c) - : "r1", "r5", "r11", "r13", "r14", - "r15", "r16", "r17", "r18", "r19"); - return __ret; -} diff --git a/arch/v850/kernel/teg.c b/arch/v850/kernel/teg.c deleted file mode 100644 index 699248f92aae..000000000000 --- a/arch/v850/kernel/teg.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * arch/v850/kernel/teg.c -- NB85E-TEG cpu chip - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "mach.h" - -void __init mach_sched_init (struct irqaction *timer_action) -{ - /* Select timer interrupt instead of external pin. */ - TEG_ISS |= 0x1; - /* Start hardware timer. */ - v850e_timer_d_configure (0, HZ); - /* Install timer interrupt handler. */ - setup_irq (IRQ_INTCMD(0), timer_action); -} - -static struct v850e_intc_irq_init irq_inits[] = { - { "IRQ", 0, NUM_CPU_IRQS, 1, 7 }, - { "CMD", IRQ_INTCMD(0), IRQ_INTCMD_NUM, 1, 5 }, - { "SER", IRQ_INTSER(0), IRQ_INTSER_NUM, 1, 3 }, - { "SR", IRQ_INTSR(0), IRQ_INTSR_NUM, 1, 4 }, - { "ST", IRQ_INTST(0), IRQ_INTST_NUM, 1, 5 }, - { 0 } -}; -#define NUM_IRQ_INITS (ARRAY_SIZE(irq_inits) - 1) - -static struct hw_interrupt_type hw_itypes[NUM_IRQ_INITS]; - -/* Initialize MA chip interrupts. */ -void __init teg_init_irqs (void) -{ - v850e_intc_init_irq_types (irq_inits, hw_itypes); -} - -/* Called before configuring an on-chip UART. */ -void teg_uart_pre_configure (unsigned chan, unsigned cflags, unsigned baud) -{ - /* Enable UART I/O pins instead of external interrupt pins, and - UART interrupts instead of external pin interrupts. */ - TEG_ISS |= 0x4E; -} diff --git a/arch/v850/kernel/time.c b/arch/v850/kernel/time.c deleted file mode 100644 index d810c93fe665..000000000000 --- a/arch/v850/kernel/time.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * linux/arch/v850/kernel/time.c -- Arch-dependent timer functions - * - * Copyright (C) 1991, 1992, 1995, 2001, 2002 Linus Torvalds - * - * This file contains the v850-specific time handling details. - * Most of the stuff is located in the machine specific files. - * - * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 - * "A Kernel Model for Precision Timekeeping" by Dave Mills - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "mach.h" - -#define TICK_SIZE (tick_nsec / 1000) - -/* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick - */ -static irqreturn_t timer_interrupt (int irq, void *dummy, struct pt_regs *regs) -{ -#if 0 - /* last time the cmos clock got updated */ - static long last_rtc_update=0; -#endif - - /* may need to kick the hardware timer */ - if (mach_tick) - mach_tick (); - - do_timer (1); -#ifndef CONFIG_SMP - update_process_times(user_mode(regs)); -#endif - profile_tick(CPU_PROFILING, regs); -#if 0 - /* - * If we have an externally synchronized Linux clock, then update - * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be - * called as close as possible to 500 ms before the new second starts. - */ - if (ntp_synced() && - xtime.tv_sec > last_rtc_update + 660 && - (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && - (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { - if (set_rtc_mmss (xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ - } -#ifdef CONFIG_HEARTBEAT - /* use power LED as a heartbeat instead -- much more useful - for debugging -- based on the version for PReP by Cort */ - /* acts like an actual heart beat -- ie thump-thump-pause... */ - if (mach_heartbeat) { - static unsigned cnt = 0, period = 0, dist = 0; - - if (cnt == 0 || cnt == dist) - mach_heartbeat ( 1 ); - else if (cnt == 7 || cnt == dist+7) - mach_heartbeat ( 0 ); - - if (++cnt > period) { - cnt = 0; - /* The hyperbolic function below modifies the heartbeat period - * length in dependency of the current (5min) load. It goes - * through the points f(0)=126, f(1)=86, f(5)=51, - * f(inf)->30. */ - period = ((672< -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -extern void *trap_table; -EXPORT_SYMBOL (trap_table); - -/* platform dependent support */ -EXPORT_SYMBOL (kernel_thread); -EXPORT_SYMBOL (__bug); - -/* Networking helper routines. */ -EXPORT_SYMBOL (csum_partial_copy_nocheck); -EXPORT_SYMBOL (csum_partial_copy_from_user); -EXPORT_SYMBOL (ip_compute_csum); -EXPORT_SYMBOL (ip_fast_csum); - -/* string / mem functions */ -EXPORT_SYMBOL (memset); -EXPORT_SYMBOL (memcpy); -EXPORT_SYMBOL (memmove); - -/* - * libgcc functions - functions that are used internally by the - * compiler... (prototypes are not correct though, but that - * doesn't really matter since they're not versioned). - */ -extern void __ashldi3 (void); -extern void __ashrdi3 (void); -extern void __lshrdi3 (void); -extern void __muldi3 (void); -extern void __negdi2 (void); - -EXPORT_SYMBOL (__ashldi3); -EXPORT_SYMBOL (__ashrdi3); -EXPORT_SYMBOL (__lshrdi3); -EXPORT_SYMBOL (__muldi3); -EXPORT_SYMBOL (__negdi2); diff --git a/arch/v850/kernel/v850e2_cache.c b/arch/v850/kernel/v850e2_cache.c deleted file mode 100644 index 4570312c689c..000000000000 --- a/arch/v850/kernel/v850e2_cache.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * arch/v850/kernel/v850e2_cache.c -- Cache control for V850E2 cache - * memories - * - * Copyright (C) 2003 NEC Electronics Corporation - * Copyright (C) 2003 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include - -#include - -/* Cache operations we can do. The encoding corresponds directly to the - value we need to write into the COPR register. */ -enum cache_op { - OP_SYNC_IF_DIRTY = V850E2_CACHE_COPR_CFC(0), /* 000 */ - OP_SYNC_IF_VALID = V850E2_CACHE_COPR_CFC(1), /* 001 */ - OP_SYNC_IF_VALID_AND_CLEAR = V850E2_CACHE_COPR_CFC(3), /* 011 */ - OP_WAY_CLEAR = V850E2_CACHE_COPR_CFC(4), /* 100 */ - OP_FILL = V850E2_CACHE_COPR_CFC(5), /* 101 */ - OP_CLEAR = V850E2_CACHE_COPR_CFC(6), /* 110 */ - OP_CREATE_DIRTY = V850E2_CACHE_COPR_CFC(7) /* 111 */ -}; - -/* Which cache to use. This encoding also corresponds directly to the - value we need to write into the COPR register. */ -enum cache { - ICACHE = 0, - DCACHE = V850E2_CACHE_COPR_LBSL -}; - -/* Returns ADDR rounded down to the beginning of its cache-line. */ -#define CACHE_LINE_ADDR(addr) \ - ((addr) & ~(V850E2_CACHE_LINE_SIZE - 1)) -/* Returns END_ADDR rounded up to the `limit' of its cache-line. */ -#define CACHE_LINE_END_ADDR(end_addr) \ - CACHE_LINE_ADDR(end_addr + (V850E2_CACHE_LINE_SIZE - 1)) - - -/* Low-level cache ops. */ - -/* Apply cache-op OP to all entries in CACHE. */ -static inline void cache_op_all (enum cache_op op, enum cache cache) -{ - int cmd = op | cache | V850E2_CACHE_COPR_WSLE | V850E2_CACHE_COPR_STRT; - - if (op != OP_WAY_CLEAR) { - /* The WAY_CLEAR operation does the whole way, but other - ops take begin-index and count params; we just indicate - the entire cache. */ - V850E2_CACHE_CADL = 0; - V850E2_CACHE_CADH = 0; - V850E2_CACHE_CCNT = V850E2_CACHE_WAY_SIZE - 1; - } - - V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(0); /* way 0 */ - V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(1); /* way 1 */ - V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(2); /* way 2 */ - V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(3); /* way 3 */ -} - -/* Apply cache-op OP to all entries in CACHE covering addresses ADDR - through ADDR+LEN. */ -static inline void cache_op_range (enum cache_op op, u32 addr, u32 len, - enum cache cache) -{ - u32 start = CACHE_LINE_ADDR (addr); - u32 end = CACHE_LINE_END_ADDR (addr + len); - u32 num_lines = (end - start) >> V850E2_CACHE_LINE_SIZE_BITS; - - V850E2_CACHE_CADL = start & 0xFFFF; - V850E2_CACHE_CADH = start >> 16; - V850E2_CACHE_CCNT = num_lines - 1; - - V850E2_CACHE_COPR = op | cache | V850E2_CACHE_COPR_STRT; -} - - -/* High-level ops. */ - -static void cache_exec_after_store_all (void) -{ - cache_op_all (OP_SYNC_IF_DIRTY, DCACHE); - cache_op_all (OP_WAY_CLEAR, ICACHE); -} - -static void cache_exec_after_store_range (u32 start, u32 len) -{ - cache_op_range (OP_SYNC_IF_DIRTY, start, len, DCACHE); - cache_op_range (OP_CLEAR, start, len, ICACHE); -} - - -/* Exported functions. */ - -void flush_icache (void) -{ - cache_exec_after_store_all (); -} - -void flush_icache_range (unsigned long start, unsigned long end) -{ - cache_exec_after_store_range (start, end - start); -} - -void flush_icache_page (struct vm_area_struct *vma, struct page *page) -{ - cache_exec_after_store_range (page_to_virt (page), PAGE_SIZE); -} - -void flush_icache_user_range (struct vm_area_struct *vma, struct page *page, - unsigned long addr, int len) -{ - cache_exec_after_store_range (addr, len); -} - -void flush_cache_sigtramp (unsigned long addr) -{ - /* For the exact size, see signal.c, but 16 bytes should be enough. */ - cache_exec_after_store_range (addr, 16); -} diff --git a/arch/v850/kernel/v850e_cache.c b/arch/v850/kernel/v850e_cache.c deleted file mode 100644 index ea3e51cfb259..000000000000 --- a/arch/v850/kernel/v850e_cache.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * arch/v850/kernel/v850e_cache.c -- Cache control for V850E cache memories - * - * Copyright (C) 2003 NEC Electronics Corporation - * Copyright (C) 2003 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -/* This file implements cache control for the rather simple cache used on - some V850E CPUs, specifically the NB85E/TEG CPU-core and the V850E/ME2 - CPU. V850E2 processors have their own (better) cache - implementation. */ - -#include -#include -#include - -#define WAIT_UNTIL_CLEAR(value) while (value) {} - -/* Set caching params via the BHC and DCC registers. */ -void v850e_cache_enable (u16 bhc, u16 icc, u16 dcc) -{ - unsigned long *r0_ram = (unsigned long *)R0_RAM_ADDR; - register u16 bhc_val asm ("r6") = bhc; - - /* Read the instruction cache control register (ICC) and confirm - that bits 0 and 1 (TCLR0, TCLR1) are all cleared. */ - WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3); - V850E_CACHE_ICC = icc; - -#ifdef V850E_CACHE_DCC - /* Configure data-cache. */ - V850E_CACHE_DCC = dcc; -#endif /* V850E_CACHE_DCC */ - - /* Configure caching for various memory regions by writing the BHC - register. The documentation says that an instruction _cannot_ - enable/disable caching for the memory region in which the - instruction itself exists; to work around this, we store - appropriate instructions into the on-chip RAM area (which is never - cached), and briefly jump there to do the work. */ -#ifdef V850E_CACHE_WRITE_IBS - *r0_ram++ = 0xf0720760; /* st.h r0, 0xfffff072[r0] */ -#endif - *r0_ram++ = 0xf06a3760; /* st.h r6, 0xfffff06a[r0] */ - *r0_ram = 0x5640006b; /* jmp [r11] */ - - asm ("mov hilo(1f), r11; jmp [%1]; 1:;" - :: "r" (bhc_val), "r" (R0_RAM_ADDR) : "r11"); -} - -static void clear_icache (void) -{ - /* 1. Read the instruction cache control register (ICC) and confirm - that bits 0 and 1 (TCLR0, TCLR1) are all cleared. */ - WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3); - - /* 2. Read the ICC register and confirm that bit 12 (LOCK0) is - cleared. Bit 13 of the ICC register is always cleared. */ - WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x1000); - - /* 3. Set the TCLR0 and TCLR1 bits of the ICC register as follows, - when clearing way 0 and way 1 at the same time: - (a) Set the TCLR0 and TCLR1 bits. - (b) Read the TCLR0 and TCLR1 bits to confirm that these bits - are cleared. - (c) Perform (a) and (b) above again. */ - V850E_CACHE_ICC |= 0x3; - WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3); - -#ifdef V850E_CACHE_REPEAT_ICC_WRITE - /* Do it again. */ - V850E_CACHE_ICC |= 0x3; - WAIT_UNTIL_CLEAR (V850E_CACHE_ICC & 0x3); -#endif -} - -#ifdef V850E_CACHE_DCC -/* Flush or clear (or both) the data cache, depending on the value of FLAGS; - the procedure is the same for both, just the control bits used differ (and - both may be performed simultaneously). */ -static void dcache_op (unsigned short flags) -{ - /* 1. Read the data cache control register (DCC) and confirm that bits - 0, 1, 4, and 5 (DC00, DC01, DC04, DC05) are all cleared. */ - WAIT_UNTIL_CLEAR (V850E_CACHE_DCC & 0x33); - - /* 2. Clear DCC register bit 12 (DC12), bit 13 (DC13), or both - depending on the way for which tags are to be cleared. */ - V850E_CACHE_DCC &= ~0xC000; - - /* 3. Set DCC register bit 0 (DC00), bit 1 (DC01) or both depending on - the way for which tags are to be cleared. - ... - Set DCC register bit 4 (DC04), bit 5 (DC05), or both depending - on the way to be data flushed. */ - V850E_CACHE_DCC |= flags; - - /* 4. Read DCC register bit DC00, DC01 [DC04, DC05], or both depending - on the way for which tags were cleared [flushed] and confirm - that that bit is cleared. */ - WAIT_UNTIL_CLEAR (V850E_CACHE_DCC & flags); -} -#endif /* V850E_CACHE_DCC */ - -/* Flushes the contents of the dcache to memory. */ -static inline void flush_dcache (void) -{ -#ifdef V850E_CACHE_DCC - /* We only need to do something if in write-back mode. */ - if (V850E_CACHE_DCC & 0x0400) - dcache_op (0x30); -#endif /* V850E_CACHE_DCC */ -} - -/* Flushes the contents of the dcache to memory, and then clears it. */ -static inline void clear_dcache (void) -{ -#ifdef V850E_CACHE_DCC - /* We only need to do something if the dcache is enabled. */ - if (V850E_CACHE_DCC & 0x0C00) - dcache_op (0x33); -#endif /* V850E_CACHE_DCC */ -} - -/* Clears the dcache without flushing to memory first. */ -static inline void clear_dcache_no_flush (void) -{ -#ifdef V850E_CACHE_DCC - /* We only need to do something if the dcache is enabled. */ - if (V850E_CACHE_DCC & 0x0C00) - dcache_op (0x3); -#endif /* V850E_CACHE_DCC */ -} - -static inline void cache_exec_after_store (void) -{ - flush_dcache (); - clear_icache (); -} - - -/* Exported functions. */ - -void flush_icache (void) -{ - cache_exec_after_store (); -} - -void flush_icache_range (unsigned long start, unsigned long end) -{ - cache_exec_after_store (); -} - -void flush_icache_page (struct vm_area_struct *vma, struct page *page) -{ - cache_exec_after_store (); -} - -void flush_icache_user_range (struct vm_area_struct *vma, struct page *page, - unsigned long adr, int len) -{ - cache_exec_after_store (); -} - -void flush_cache_sigtramp (unsigned long addr) -{ - cache_exec_after_store (); -} diff --git a/arch/v850/kernel/v850e_intc.c b/arch/v850/kernel/v850e_intc.c deleted file mode 100644 index 8d39a52ee6d1..000000000000 --- a/arch/v850/kernel/v850e_intc.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * arch/v850/kernel/v850e_intc.c -- V850E interrupt controller (INTC) - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include -#include - -#include - -static void irq_nop (unsigned irq) { } - -static unsigned v850e_intc_irq_startup (unsigned irq) -{ - v850e_intc_clear_pending_irq (irq); - v850e_intc_enable_irq (irq); - return 0; -} - -static void v850e_intc_end_irq (unsigned irq) -{ - unsigned long psw, temp; - - /* Clear the highest-level bit in the In-service priority register - (ISPR), to allow this interrupt (or another of the same or - lesser priority) to happen again. - - The `reti' instruction normally does this automatically when the - PSW bits EP and NP are zero, but we can't always rely on reti - being used consistently to return after an interrupt (another - process can be scheduled, for instance, which can delay the - associated reti for a long time, or this process may be being - single-stepped, which uses the `dbret' instruction to return - from the kernel). - - We also set the PSW EP bit, which prevents reti from also - trying to modify the ISPR itself. */ - - /* Get PSW and disable interrupts. */ - asm volatile ("stsr psw, %0; di" : "=r" (psw)); - /* We don't want to do anything for NMIs (they don't use the ISPR). */ - if (! (psw & 0xC0)) { - /* Transition to `trap' state, so that an eventual real - reti instruction won't modify the ISPR. */ - psw |= 0x40; - /* Fake an interrupt return, which automatically clears the - appropriate bit in the ISPR. */ - asm volatile ("mov hilo(1f), %0;" - "ldsr %0, eipc; ldsr %1, eipsw;" - "reti;" - "1:" - : "=&r" (temp) : "r" (psw)); - } -} - -/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array - INITS (which is terminated by an entry with the name field == 0). */ -void __init v850e_intc_init_irq_types (struct v850e_intc_irq_init *inits, - struct hw_interrupt_type *hw_irq_types) -{ - struct v850e_intc_irq_init *init; - for (init = inits; init->name; init++) { - unsigned i; - struct hw_interrupt_type *hwit = hw_irq_types++; - - hwit->typename = init->name; - - hwit->startup = v850e_intc_irq_startup; - hwit->shutdown = v850e_intc_disable_irq; - hwit->enable = v850e_intc_enable_irq; - hwit->disable = v850e_intc_disable_irq; - hwit->ack = irq_nop; - hwit->end = v850e_intc_end_irq; - - /* Initialize kernel IRQ infrastructure for this interrupt. */ - init_irq_handlers(init->base, init->num, init->interval, hwit); - - /* Set the interrupt priorities. */ - for (i = 0; i < init->num; i++) { - unsigned irq = init->base + i * init->interval; - - /* If the interrupt is currently enabled (all - interrupts are initially disabled), then - assume whoever enabled it has set things up - properly, and avoid messing with it. */ - if (! v850e_intc_irq_enabled (irq)) - /* This write also (1) disables the - interrupt, and (2) clears any pending - interrupts. */ - V850E_INTC_IC (irq) - = (V850E_INTC_IC_PR (init->priority) - | V850E_INTC_IC_MK); - } - } -} diff --git a/arch/v850/kernel/v850e_timer_d.c b/arch/v850/kernel/v850e_timer_d.c deleted file mode 100644 index d2a4ece2574c..000000000000 --- a/arch/v850/kernel/v850e_timer_d.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * include/asm-v850/v850e_timer_d.c -- `Timer D' component often used - * with V850E CPUs - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include - -#include -#include - -/* Start interval timer TIMER (0-3). The timer will issue the - corresponding INTCMD interrupt RATE times per second. - This function does not enable the interrupt. */ -void v850e_timer_d_configure (unsigned timer, unsigned rate) -{ - unsigned divlog2, count; - - /* Calculate params for timer. */ - if (! calc_counter_params ( - V850E_TIMER_D_BASE_FREQ, rate, - V850E_TIMER_D_TMCD_CS_MIN, V850E_TIMER_D_TMCD_CS_MAX, 16, - &divlog2, &count)) - printk (KERN_WARNING - "Cannot find interval timer %d setting suitable" - " for rate of %dHz.\n" - "Using rate of %dHz instead.\n", - timer, rate, - (V850E_TIMER_D_BASE_FREQ >> divlog2) >> 16); - - /* Do the actual hardware timer initialization: */ - - /* Enable timer. */ - V850E_TIMER_D_TMCD(timer) = V850E_TIMER_D_TMCD_CAE; - /* Set clock divider. */ - V850E_TIMER_D_TMCD(timer) - = V850E_TIMER_D_TMCD_CAE - | V850E_TIMER_D_TMCD_CS(divlog2); - /* Set timer compare register. */ - V850E_TIMER_D_CMD(timer) = count; - /* Start counting. */ - V850E_TIMER_D_TMCD(timer) - = V850E_TIMER_D_TMCD_CAE - | V850E_TIMER_D_TMCD_CS(divlog2) - | V850E_TIMER_D_TMCD_CE; -} diff --git a/arch/v850/kernel/v850e_utils.c b/arch/v850/kernel/v850e_utils.c deleted file mode 100644 index e6807ef8dee6..000000000000 --- a/arch/v850/kernel/v850e_utils.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * include/asm-v850/v850e_utils.h -- Utility functions associated with - * V850E CPUs - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include - -/* Calculate counter clock-divider and count values to attain the - desired frequency RATE from the base frequency BASE_FREQ. The - counter is expected to have a clock-divider, which can divide the - system cpu clock by a power of two value from MIN_DIVLOG2 to - MAX_DIV_LOG2, and a word-size of COUNTER_SIZE bits (the counter - counts up and resets whenever it's equal to the compare register, - generating an interrupt or whatever when it does so). The returned - values are: *DIVLOG2 -- log2 of the desired clock divider and *COUNT - -- the counter compare value to use. Returns true if it was possible - to find a reasonable value, otherwise false (and the other return - values will be set to be as good as possible). */ -int calc_counter_params (unsigned long base_freq, - unsigned long rate, - unsigned min_divlog2, unsigned max_divlog2, - unsigned counter_size, - unsigned *divlog2, unsigned *count) -{ - unsigned _divlog2; - int ok = 0; - - /* Find the lowest clock divider setting that can represent RATE. */ - for (_divlog2 = min_divlog2; _divlog2 <= max_divlog2; _divlog2++) { - /* Minimum interrupt rate possible using this divider. */ - unsigned min_int_rate - = (base_freq >> _divlog2) >> counter_size; - - if (min_int_rate <= rate) { - /* This setting is the highest resolution - setting that's slow enough enough to attain - RATE interrupts per second, so use it. */ - ok = 1; - break; - } - } - - if (_divlog2 > max_divlog2) - /* Can't find correct setting. */ - _divlog2 = max_divlog2; - - if (divlog2) - *divlog2 = _divlog2; - if (count) - *count = ((base_freq >> _divlog2) + rate/2) / rate; - - return ok; -} diff --git a/arch/v850/kernel/vmlinux.lds.S b/arch/v850/kernel/vmlinux.lds.S deleted file mode 100644 index d08cd1d27f27..000000000000 --- a/arch/v850/kernel/vmlinux.lds.S +++ /dev/null @@ -1,306 +0,0 @@ -/* - * arch/v850/vmlinux.lds.S -- kernel linker script for v850 platforms - * - * Copyright (C) 2002,03,04,05 NEC Electronics Corporation - * Copyright (C) 2002,03,04,05 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - - -#define VMLINUX_SYMBOL(_sym_) _##_sym_ -#include - -/* For most platforms, this will define useful things like RAM addr/size. */ -#include - - -/* The following macros contain the usual definitions for various data areas. - The prefix `RAMK_' is used to indicate macros suitable for kernels loaded - into RAM, and similarly `ROMK_' for ROM-resident kernels. Note that all - symbols are prefixed with an extra `_' for compatibility with the v850 - toolchain. */ - - -/* Interrupt vectors. */ -#define INTV_CONTENTS \ - . = ALIGN (0x10) ; \ - __intv_start = . ; \ - *(.intv.reset) /* Reset vector */ \ - . = __intv_start + 0x10 ; \ - *(.intv.common) /* Vectors common to all v850e proc */\ - . = __intv_start + 0x80 ; \ - *(.intv.mach) /* Machine-specific int. vectors. */ \ - __intv_end = . ; - -#define RODATA_CONTENTS \ - . = ALIGN (16) ; \ - *(.rodata) *(.rodata.*) \ - *(__vermagic) /* Kernel version magic */ \ - *(.rodata1) \ - /* PCI quirks */ \ - ___start_pci_fixups_early = . ; \ - *(.pci_fixup_early) \ - ___end_pci_fixups_early = . ; \ - ___start_pci_fixups_header = . ; \ - *(.pci_fixup_header) \ - ___end_pci_fixups_header = . ; \ - ___start_pci_fixups_final = . ; \ - *(.pci_fixup_final) \ - ___end_pci_fixups_final = . ; \ - ___start_pci_fixups_enable = . ; \ - *(.pci_fixup_enable) \ - ___end_pci_fixups_enable = . ; \ - /* Kernel symbol table: Normal symbols */ \ - ___start___ksymtab = .; \ - *(__ksymtab) \ - ___stop___ksymtab = .; \ - /* Kernel symbol table: GPL-only symbols */ \ - ___start___ksymtab_gpl = .; \ - *(__ksymtab_gpl) \ - ___stop___ksymtab_gpl = .; \ - /* Kernel symbol table: GPL-future symbols */ \ - ___start___ksymtab_gpl_future = .; \ - *(__ksymtab_gpl_future) \ - ___stop___ksymtab_gpl_future = .; \ - /* Kernel symbol table: strings */ \ - *(__ksymtab_strings) \ - /* Kernel symbol table: Normal symbols */ \ - ___start___kcrctab = .; \ - *(__kcrctab) \ - ___stop___kcrctab = .; \ - /* Kernel symbol table: GPL-only symbols */ \ - ___start___kcrctab_gpl = .; \ - *(__kcrctab_gpl) \ - ___stop___kcrctab_gpl = .; \ - /* Kernel symbol table: GPL-future symbols */ \ - ___start___kcrctab_gpl_future = .; \ - *(__kcrctab_gpl_future) \ - ___stop___kcrctab_gpl_future = .; \ - /* Built-in module parameters */ \ - . = ALIGN (4) ; \ - ___start___param = .; \ - *(__param) \ - ___stop___param = .; - - -/* Kernel text segment, and some constant data areas. */ -#define TEXT_CONTENTS \ - _text = .; \ - __stext = . ; \ - TEXT_TEXT \ - SCHED_TEXT \ - *(.exit.text) /* 2.5 convention */ \ - *(.text.exit) /* 2.4 convention */ \ - *(.text.lock) \ - *(.exitcall.exit) \ - __real_etext = . ; /* There may be data after here. */ \ - RODATA_CONTENTS \ - . = ALIGN (4) ; \ - *(.call_table_data) \ - *(.call_table_text) \ - . = ALIGN (16) ; /* Exception table. */ \ - ___start___ex_table = . ; \ - *(__ex_table) \ - ___stop___ex_table = . ; \ - . = ALIGN (4) ; \ - __etext = . ; - -/* Kernel data segment. */ -#define DATA_CONTENTS \ - __sdata = . ; \ - DATA_DATA \ - EXIT_DATA /* 2.5 convention */ \ - *(.data.exit) /* 2.4 convention */ \ - . = ALIGN (16) ; \ - *(.data.cacheline_aligned) \ - . = ALIGN (0x2000) ; \ - *(.data.init_task) \ - . = ALIGN (0x2000) ; \ - __edata = . ; - -/* Kernel BSS segment. */ -#define BSS_CONTENTS \ - __sbss = . ; \ - *(.bss) \ - *(COMMON) \ - . = ALIGN (4) ; \ - __init_stack_end = . ; \ - __ebss = . ; - -/* `initcall' tables. */ -#define INITCALL_CONTENTS \ - . = ALIGN (16) ; \ - ___setup_start = . ; \ - *(.init.setup) /* 2.5 convention */ \ - *(.setup.init) /* 2.4 convention */ \ - ___setup_end = . ; \ - ___initcall_start = . ; \ - *(.initcall.init) \ - INITCALLS \ - . = ALIGN (4) ; \ - ___initcall_end = . ; \ - ___con_initcall_start = .; \ - *(.con_initcall.init) \ - ___con_initcall_end = .; - -/* Contents of `init' section for a kernel that's loaded into RAM. */ -#define RAMK_INIT_CONTENTS \ - RAMK_INIT_CONTENTS_NO_END \ - __init_end = . ; -/* Same as RAMK_INIT_CONTENTS, but doesn't define the `__init_end' symbol. */ -#define RAMK_INIT_CONTENTS_NO_END \ - . = ALIGN (4096) ; \ - __init_start = . ; \ - __sinittext = .; \ - INIT_TEXT /* 2.5 convention */ \ - __einittext = .; \ - INIT_DATA \ - *(.text.init) /* 2.4 convention */ \ - *(.data.init) \ - INITCALL_CONTENTS \ - INITRAMFS_CONTENTS - -/* The contents of `init' section for a ROM-resident kernel which - should go into RAM. */ -#define ROMK_INIT_RAM_CONTENTS \ - . = ALIGN (4096) ; \ - __init_start = . ; \ - INIT_DATA /* 2.5 convention */ \ - *(.data.init) /* 2.4 convention */ \ - __init_end = . ; \ - . = ALIGN (4096) ; - -/* The contents of `init' section for a ROM-resident kernel which - should go into ROM. */ -#define ROMK_INIT_ROM_CONTENTS \ - _sinittext = .; \ - INIT_TEXT /* 2.5 convention */ \ - _einittext = .; \ - *(.text.init) /* 2.4 convention */ \ - INITCALL_CONTENTS \ - INITRAMFS_CONTENTS - -/* A root filesystem image, for kernels with an embedded root filesystem. */ -#define ROOT_FS_CONTENTS \ - __root_fs_image_start = . ; \ - *(.root) \ - __root_fs_image_end = . ; - -#ifdef CONFIG_BLK_DEV_INITRD -/* The initramfs archive. */ -#define INITRAMFS_CONTENTS \ - . = ALIGN (4) ; \ - ___initramfs_start = . ; \ - *(.init.ramfs) \ - ___initramfs_end = . ; -#endif - -/* Where the initial bootmap (bitmap for the boot-time memory allocator) - should be place. */ -#define BOOTMAP_CONTENTS \ - . = ALIGN (4096) ; \ - __bootmap = . ; \ - . = . + 4096 ; /* enough for 128MB. */ - -/* The contents of a `typical' kram area for a kernel in RAM. */ -#define RAMK_KRAM_CONTENTS \ - __kram_start = . ; \ - TEXT_CONTENTS \ - DATA_CONTENTS \ - BSS_CONTENTS \ - RAMK_INIT_CONTENTS \ - __kram_end = . ; \ - BOOTMAP_CONTENTS - - -/* Define output sections normally used for a ROM-resident kernel. - ROM and RAM should be appropriate memory areas to use for kernel - ROM and RAM data. This assumes that ROM starts at 0 (and thus can - hold the interrupt vectors). */ -#define ROMK_SECTIONS(ROM, RAM) \ - .rom : { \ - INTV_CONTENTS \ - TEXT_CONTENTS \ - ROMK_INIT_ROM_CONTENTS \ - ROOT_FS_CONTENTS \ - } > ROM \ - \ - __rom_copy_src_start = . ; \ - \ - .data : { \ - __kram_start = . ; \ - __rom_copy_dst_start = . ; \ - DATA_CONTENTS \ - ROMK_INIT_RAM_CONTENTS \ - __rom_copy_dst_end = . ; \ - } > RAM AT> ROM \ - \ - .bss ALIGN (4) : { \ - BSS_CONTENTS \ - __kram_end = . ; \ - BOOTMAP_CONTENTS \ - } > RAM - - -/* The 32-bit variable `jiffies' is just the lower 32-bits of `jiffies_64'. */ -_jiffies = _jiffies_64 ; - - -/* Include an appropriate platform-dependent linker-script (which - usually should use the above macros to do most of the work). */ - -#ifdef CONFIG_V850E_SIM -# include "sim.ld" -#endif - -#ifdef CONFIG_V850E2_SIM85E2 -# include "sim85e2.ld" -#endif - -#ifdef CONFIG_V850E2_FPGA85E2C -# include "fpga85e2c.ld" -#endif - -#ifdef CONFIG_V850E2_ANNA -# ifdef CONFIG_ROM_KERNEL -# include "anna-rom.ld" -# else -# include "anna.ld" -# endif -#endif - -#ifdef CONFIG_V850E_AS85EP1 -# ifdef CONFIG_ROM_KERNEL -# include "as85ep1-rom.ld" -# else -# include "as85ep1.ld" -# endif -#endif - -#ifdef CONFIG_RTE_CB_MA1 -# ifdef CONFIG_ROM_KERNEL -# include "rte_ma1_cb-rom.ld" -# else -# include "rte_ma1_cb.ld" -# endif -#endif - -#ifdef CONFIG_RTE_CB_NB85E -# ifdef CONFIG_ROM_KERNEL -# include "rte_nb85e_cb-rom.ld" -# elif defined(CONFIG_RTE_CB_MULTI) -# include "rte_nb85e_cb-multi.ld" -# else -# include "rte_nb85e_cb.ld" -# endif -#endif - -#ifdef CONFIG_RTE_CB_ME2 -# include "rte_me2_cb.ld" -#endif - diff --git a/arch/v850/lib/Makefile b/arch/v850/lib/Makefile deleted file mode 100644 index 1c78b728a117..000000000000 --- a/arch/v850/lib/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# -# arch/v850/lib/Makefile -# - -lib-y = ashrdi3.o ashldi3.o lshrdi3.o muldi3.o negdi2.o \ - checksum.o memcpy.o memset.o diff --git a/arch/v850/lib/ashldi3.c b/arch/v850/lib/ashldi3.c deleted file mode 100644 index 9e792d53f0e4..000000000000 --- a/arch/v850/lib/ashldi3.c +++ /dev/null @@ -1,62 +0,0 @@ -/* ashldi3.c extracted from gcc-2.95.2/libgcc2.c which is: */ -/* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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, or (at your option) -any later version. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#define BITS_PER_UNIT 8 - -typedef int SItype __attribute__ ((mode (SI))); -typedef unsigned int USItype __attribute__ ((mode (SI))); -typedef int DItype __attribute__ ((mode (DI))); -typedef int word_type __attribute__ ((mode (__word__))); - -struct DIstruct {SItype high, low;}; - -typedef union -{ - struct DIstruct s; - DItype ll; -} DIunion; - -DItype -__ashldi3 (DItype u, word_type b) -{ - DIunion w; - word_type bm; - DIunion uu; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (SItype) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.low = 0; - w.s.high = (USItype)uu.s.low << -bm; - } - else - { - USItype carries = (USItype)uu.s.low >> bm; - w.s.low = (USItype)uu.s.low << b; - w.s.high = ((USItype)uu.s.high << b) | carries; - } - - return w.ll; -} diff --git a/arch/v850/lib/ashrdi3.c b/arch/v850/lib/ashrdi3.c deleted file mode 100644 index 78efb65e315a..000000000000 --- a/arch/v850/lib/ashrdi3.c +++ /dev/null @@ -1,63 +0,0 @@ -/* ashrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ -/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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, or (at your option) -any later version. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#define BITS_PER_UNIT 8 - -typedef int SItype __attribute__ ((mode (SI))); -typedef unsigned int USItype __attribute__ ((mode (SI))); -typedef int DItype __attribute__ ((mode (DI))); -typedef int word_type __attribute__ ((mode (__word__))); - -struct DIstruct {SItype high, low;}; - -typedef union -{ - struct DIstruct s; - DItype ll; -} DIunion; - -DItype -__ashrdi3 (DItype u, word_type b) -{ - DIunion w; - word_type bm; - DIunion uu; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (SItype) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - /* w.s.high = 1..1 or 0..0 */ - w.s.high = uu.s.high >> (sizeof (SItype) * BITS_PER_UNIT - 1); - w.s.low = uu.s.high >> -bm; - } - else - { - USItype carries = (USItype)uu.s.high << bm; - w.s.high = uu.s.high >> b; - w.s.low = ((USItype)uu.s.low >> b) | carries; - } - - return w.ll; -} diff --git a/arch/v850/lib/checksum.c b/arch/v850/lib/checksum.c deleted file mode 100644 index 042158dfe17a..000000000000 --- a/arch/v850/lib/checksum.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * MIPS specific IP/TCP/UDP checksumming routines - * - * Authors: Ralf Baechle, - * Lots of code moved from tcp.c and ip.c; see those files - * for more names. - * - * 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. - * - * $Id: checksum.c,v 1.1 2002/09/28 14:58:40 gerg Exp $ - */ -#include -#include -#include -#include -#include -#include - -static inline unsigned short from32to16 (unsigned long sum) -{ - unsigned int result; - /* - %0 %1 - hsw %1, %0 H L L H - add %1, %0 H L H+L+C H+L - */ - asm ("hsw %1, %0; add %1, %0" : "=&r" (result) : "r" (sum)); - return result >> 16; -} - -static inline unsigned int do_csum(const unsigned char * buff, int len) -{ - int odd, count; - unsigned int result = 0; - - if (len <= 0) - goto out; - odd = 1 & (unsigned long) buff; - if (odd) { - result = be16_to_cpu(*buff); - len--; - buff++; - } - count = len >> 1; /* nr of 16-bit words.. */ - if (count) { - if (2 & (unsigned long) buff) { - result += *(unsigned short *) buff; - count--; - len -= 2; - buff += 2; - } - count >>= 1; /* nr of 32-bit words.. */ - if (count) { - unsigned int carry = 0; - do { - unsigned int w = *(unsigned int *) buff; - count--; - buff += 4; - result += carry; - result += w; - carry = (w > result); - } while (count); - result += carry; - result = (result & 0xffff) + (result >> 16); - } - if (len & 2) { - result += *(unsigned short *) buff; - buff += 2; - } - } - if (len & 1) - result += le16_to_cpu(*buff); - result = from32to16(result); - if (odd) - result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); -out: - return result; -} - -/* - * This is a version of ip_compute_csum() optimized for IP headers, - * which always checksum on 4 octet boundaries. - */ -__sum16 ip_fast_csum(const void *iph, unsigned int ihl) -{ - return (__force __sum16)~do_csum(iph,ihl*4); -} - -/* - * this routine is used for miscellaneous IP-like checksums, mainly - * in icmp.c - */ -__sum16 ip_compute_csum(const void *buff, int len) -{ - return (__force __sum16)~do_csum(buff,len); -} - -/* - * computes a partial checksum, e.g. for TCP/UDP fragments - */ -__wsum csum_partial(const void *buff, int len, __wsum sum) -{ - unsigned int result = do_csum(buff, len); - - /* add in old sum, and carry.. */ - result += (__force u32)sum; - if ((__force u32)sum > result) - result += 1; - return (__force __wsum)result; -} - -EXPORT_SYMBOL(csum_partial); - -/* - * copy while checksumming, otherwise like csum_partial - */ -__wsum csum_partial_copy_nocheck(const void *src, void *dst, - int len, __wsum sum) -{ - /* - * It's 2:30 am and I don't feel like doing it real ... - * This is lots slower than the real thing (tm) - */ - sum = csum_partial(src, len, sum); - memcpy(dst, src, len); - - return sum; -} - -/* - * Copy from userspace and compute checksum. If we catch an exception - * then zero the rest of the buffer. - */ -__wsum csum_partial_copy_from_user (const void *src, - void *dst, - int len, __wsum sum, - int *err_ptr) -{ - int missing; - - missing = copy_from_user(dst, src, len); - if (missing) { - memset(dst + len - missing, 0, missing); - *err_ptr = -EFAULT; - } - - return csum_partial(dst, len, sum); -} diff --git a/arch/v850/lib/lshrdi3.c b/arch/v850/lib/lshrdi3.c deleted file mode 100644 index 93b1cb6fdee8..000000000000 --- a/arch/v850/lib/lshrdi3.c +++ /dev/null @@ -1,62 +0,0 @@ -/* lshrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ -/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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, or (at your option) -any later version. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#define BITS_PER_UNIT 8 - -typedef int SItype __attribute__ ((mode (SI))); -typedef unsigned int USItype __attribute__ ((mode (SI))); -typedef int DItype __attribute__ ((mode (DI))); -typedef int word_type __attribute__ ((mode (__word__))); - -struct DIstruct {SItype high, low;}; - -typedef union -{ - struct DIstruct s; - DItype ll; -} DIunion; - -DItype -__lshrdi3 (DItype u, word_type b) -{ - DIunion w; - word_type bm; - DIunion uu; - - if (b == 0) - return u; - - uu.ll = u; - - bm = (sizeof (SItype) * BITS_PER_UNIT) - b; - if (bm <= 0) - { - w.s.high = 0; - w.s.low = (USItype)uu.s.high >> -bm; - } - else - { - USItype carries = (USItype)uu.s.high << bm; - w.s.high = (USItype)uu.s.high >> b; - w.s.low = ((USItype)uu.s.low >> b) | carries; - } - - return w.ll; -} diff --git a/arch/v850/lib/memcpy.c b/arch/v850/lib/memcpy.c deleted file mode 100644 index 492847b3e612..000000000000 --- a/arch/v850/lib/memcpy.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * arch/v850/lib/memcpy.c -- Memory copying - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include -#include - -#define CHUNK_SIZE 32 /* bytes */ -#define CHUNK_ALIGNED(addr) (((unsigned long)addr & 0x3) == 0) - -/* Note that this macro uses 8 call-clobbered registers (not including - R1), which are few enough so that the following functions don't need - to spill anything to memory. It also uses R1, which is nominally - reserved for the assembler, but here it should be OK. */ -#define COPY_CHUNK(src, dst) \ - asm ("mov %0, ep;" \ - "sld.w 0[ep], r1; sld.w 4[ep], r12;" \ - "sld.w 8[ep], r13; sld.w 12[ep], r14;" \ - "sld.w 16[ep], r15; sld.w 20[ep], r17;" \ - "sld.w 24[ep], r18; sld.w 28[ep], r19;" \ - "mov %1, ep;" \ - "sst.w r1, 0[ep]; sst.w r12, 4[ep];" \ - "sst.w r13, 8[ep]; sst.w r14, 12[ep];" \ - "sst.w r15, 16[ep]; sst.w r17, 20[ep];" \ - "sst.w r18, 24[ep]; sst.w r19, 28[ep]" \ - :: "r" (src), "r" (dst) \ - : "r1", "r12", "r13", "r14", "r15", \ - "r17", "r18", "r19", "ep", "memory"); - -void *memcpy (void *dst, const void *src, __kernel_size_t size) -{ - char *_dst = dst; - const char *_src = src; - - if (size >= CHUNK_SIZE && CHUNK_ALIGNED(_src) && CHUNK_ALIGNED(_dst)) { - /* Copy large blocks efficiently. */ - unsigned count; - for (count = size / CHUNK_SIZE; count; count--) { - COPY_CHUNK (_src, _dst); - _src += CHUNK_SIZE; - _dst += CHUNK_SIZE; - } - size %= CHUNK_SIZE; - } - - if (size > 0) - do - *_dst++ = *_src++; - while (--size); - - return dst; -} - -void *memmove (void *dst, const void *src, __kernel_size_t size) -{ - if ((unsigned long)dst < (unsigned long)src - || (unsigned long)src + size < (unsigned long)dst) - return memcpy (dst, src, size); - else { - char *_dst = dst + size; - const char *_src = src + size; - - if (size >= CHUNK_SIZE - && CHUNK_ALIGNED (_src) && CHUNK_ALIGNED (_dst)) - { - /* Copy large blocks efficiently. */ - unsigned count; - for (count = size / CHUNK_SIZE; count; count--) { - _src -= CHUNK_SIZE; - _dst -= CHUNK_SIZE; - COPY_CHUNK (_src, _dst); - } - size %= CHUNK_SIZE; - } - - if (size > 0) - do - *--_dst = *--_src; - while (--size); - - return _dst; - } -} diff --git a/arch/v850/lib/memset.c b/arch/v850/lib/memset.c deleted file mode 100644 index d1b2ad821b15..000000000000 --- a/arch/v850/lib/memset.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * arch/v850/lib/memset.c -- Memory initialization - * - * Copyright (C) 2001,02,04 NEC Corporation - * Copyright (C) 2001,02,04 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#include - -void *memset (void *dst, int val, __kernel_size_t count) -{ - if (count) { - register unsigned loop; - register void *ptr asm ("ep") = dst; - - /* replicate VAL into a long. */ - val &= 0xff; - val |= val << 8; - val |= val << 16; - - /* copy initial unaligned bytes. */ - if ((long)ptr & 1) { - *(char *)ptr = val; - ptr = (void *)((char *)ptr + 1); - count--; - } - if (count > 2 && ((long)ptr & 2)) { - *(short *)ptr = val; - ptr = (void *)((short *)ptr + 1); - count -= 2; - } - - /* 32-byte copying loop. */ - for (loop = count / 32; loop; loop--) { - asm ("sst.w %0, 0[ep]; sst.w %0, 4[ep];" - "sst.w %0, 8[ep]; sst.w %0, 12[ep];" - "sst.w %0, 16[ep]; sst.w %0, 20[ep];" - "sst.w %0, 24[ep]; sst.w %0, 28[ep]" - :: "r" (val) : "memory"); - ptr += 32; - } - count %= 32; - - /* long copying loop. */ - for (loop = count / 4; loop; loop--) { - *(long *)ptr = val; - ptr = (void *)((long *)ptr + 1); - } - count %= 4; - - /* finish up with any trailing bytes. */ - if (count & 2) { - *(short *)ptr = val; - ptr = (void *)((short *)ptr + 1); - } - if (count & 1) { - *(char *)ptr = val; - } - } - - return dst; -} diff --git a/arch/v850/lib/muldi3.c b/arch/v850/lib/muldi3.c deleted file mode 100644 index 277ca25c82c8..000000000000 --- a/arch/v850/lib/muldi3.c +++ /dev/null @@ -1,61 +0,0 @@ -/* muldi3.c extracted from gcc-2.7.2.3/libgcc2.c and - gcc-2.7.2.3/longlong.h which is: */ -/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 2001 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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, or (at your option) -any later version. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -#define umul_ppmm(w1, w0, u, v) \ - __asm__ ("mulu %3, %0, %1" \ - : "=r" ((USItype)(w0)), \ - "=r" ((USItype)(w1)) \ - : "%0" ((USItype)(u)), \ - "r" ((USItype)(v))) - -#define __umulsidi3(u, v) \ - ({DIunion __w; \ - umul_ppmm (__w.s.high, __w.s.low, u, v); \ - __w.ll; }) - -typedef int SItype __attribute__ ((mode (SI))); -typedef unsigned int USItype __attribute__ ((mode (SI))); -typedef int DItype __attribute__ ((mode (DI))); -typedef int word_type __attribute__ ((mode (__word__))); - -struct DIstruct {SItype high, low;}; - -typedef union -{ - struct DIstruct s; - DItype ll; -} DIunion; - -DItype -__muldi3 (DItype u, DItype v) -{ - DIunion w; - DIunion uu, vv; - - uu.ll = u, - vv.ll = v; - - w.ll = __umulsidi3 (uu.s.low, vv.s.low); - w.s.high += ((USItype) uu.s.low * (USItype) vv.s.high - + (USItype) uu.s.high * (USItype) vv.s.low); - - return w.ll; -} diff --git a/arch/v850/lib/negdi2.c b/arch/v850/lib/negdi2.c deleted file mode 100644 index 571e04fc619a..000000000000 --- a/arch/v850/lib/negdi2.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * arch/v850/lib/negdi2.c -- 64-bit negation - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -typedef int DItype __attribute__ ((mode (DI))); - -DItype __negdi2 (DItype x) -{ - __asm__ __volatile__ - ("not r6, r10;" - "add 1, r10;" - "setf c, r6;" - "not r7, r11;" - "add r6, r11" - ::: "r6", "r7", "r10", "r11"); -} diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 8fc7451c0049..3b4a14e355c1 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -942,22 +942,6 @@ config SERIAL_IP22_ZILOG_CONSOLE depends on SERIAL_IP22_ZILOG=y select SERIAL_CORE_CONSOLE -config V850E_UART - bool "NEC V850E on-chip UART support" - depends on V850E_MA1 || V850E_ME2 || V850E_TEG || V850E2_ANNA || V850E_AS85EP1 - select SERIAL_CORE - default y - -config V850E_UARTB - bool - depends on V850E_UART && V850E_ME2 - default y - -config V850E_UART_CONSOLE - bool "Use NEC V850E on-chip UART for console" - depends on V850E_UART - select SERIAL_CORE_CONSOLE - config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" depends on SUPERH || H8300 diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ccb78f66c2b6..48399e134c0d 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -788,8 +788,6 @@ config WATCHDOG_RIO machines. The watchdog timeout period is normally one minute but can be changed with a boot-time parameter. -# V850 Architecture - # XTENSA Architecture # diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 25b352b664d9..edd305a64e63 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -119,8 +119,6 @@ obj-$(CONFIG_SH_WDT) += shwdt.o # SPARC64 Architecture -# V850 Architecture - # XTENSA Architecture # Architecture Independant diff --git a/include/asm-v850/Kbuild b/include/asm-v850/Kbuild deleted file mode 100644 index c68e1680da01..000000000000 --- a/include/asm-v850/Kbuild +++ /dev/null @@ -1 +0,0 @@ -include include/asm-generic/Kbuild.asm diff --git a/include/asm-v850/a.out.h b/include/asm-v850/a.out.h deleted file mode 100644 index e9439a0708f6..000000000000 --- a/include/asm-v850/a.out.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __V850_A_OUT_H__ -#define __V850_A_OUT_H__ - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - - -#endif /* __V850_A_OUT_H__ */ diff --git a/include/asm-v850/anna.h b/include/asm-v850/anna.h deleted file mode 100644 index cd5eaee103b0..000000000000 --- a/include/asm-v850/anna.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * include/asm-v850/anna.h -- Anna V850E2 evaluation cpu chip/board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_ANNA_H__ -#define __V850_ANNA_H__ - -#include /* Based on V850E2 core. */ - - -#define CPU_MODEL "v850e2/anna" -#define CPU_MODEL_LONG "NEC V850E2/Anna" -#define PLATFORM "anna" -#define PLATFORM_LONG "NEC/Midas lab V850E2/Anna evaluation board" - -#define CPU_CLOCK_FREQ 200000000 /* 200MHz */ -#define SYS_CLOCK_FREQ 33300000 /* 33.3MHz */ - - -/* 1MB of static RAM. This memory is mirrored 64 times. */ -#define SRAM_ADDR 0x04000000 -#define SRAM_SIZE 0x00100000 /* 1MB */ -/* 64MB of DRAM. */ -#define SDRAM_ADDR 0x08000000 -#define SDRAM_SIZE 0x04000000 /* 64MB */ - - -/* For */ -#define PAGE_OFFSET SRAM_ADDR - -/* We use on-chip RAM, for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. The Anna chip has - 128K of `dLB' ram nominally located at 0xFFF00000, but it's mirrored - every 128K, so we can use the `last mirror' (except for the portion at - the top which is overridden by I/O space). In addition, the early - sample chip we're using has lots of memory errors in the dLB ram, so we - use a specially chosen location that has at least 20 bytes of contiguous - valid memory (xxxF0020 - xxxF003F). */ -#define R0_RAM_ADDR 0xFFFF8020 - - -/* Anna specific control registers. */ -#define ANNA_ILBEN_ADDR 0xFFFFF7F2 -#define ANNA_ILBEN (*(volatile u16 *)ANNA_ILBEN_ADDR) - - -/* I/O port P0-P3. */ -/* Direct I/O. Bits 0-7 are pins Pn0-Pn7. */ -#define ANNA_PORT_IO_ADDR(n) (0xFFFFF400 + (n) * 2) -#define ANNA_PORT_IO(n) (*(volatile u8 *)ANNA_PORT_IO_ADDR(n)) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ANNA_PORT_PM_ADDR(n) (0xFFFFF410 + (n) * 2) -#define ANNA_PORT_PM(n) (*(volatile u8 *)ANNA_PORT_PM_ADDR(n)) - - -/* Hardware-specific interrupt numbers (in the kernel IRQ namespace). */ -#define IRQ_INTP(n) (n) /* Pnnn (pin) interrupts 0-15 */ -#define IRQ_INTP_NUM 16 -#define IRQ_INTOV(n) (0x10 + (n)) /* 0-2 */ -#define IRQ_INTOV_NUM 2 -#define IRQ_INTCCC(n) (0x12 + (n)) -#define IRQ_INTCCC_NUM 4 -#define IRQ_INTCMD(n) (0x16 + (n)) /* interval timer interrupts 0-5 */ -#define IRQ_INTCMD_NUM 6 -#define IRQ_INTDMA(n) (0x1C + (n)) /* DMA interrupts 0-3 */ -#define IRQ_INTDMA_NUM 4 -#define IRQ_INTDMXER 0x20 -#define IRQ_INTSRE(n) (0x21 + (n)*3) /* UART 0-1 reception error */ -#define IRQ_INTSRE_NUM 2 -#define IRQ_INTSR(n) (0x22 + (n)*3) /* UART 0-1 reception completion */ -#define IRQ_INTSR_NUM 2 -#define IRQ_INTST(n) (0x23 + (n)*3) /* UART 0-1 transmission completion */ -#define IRQ_INTST_NUM 2 - -#define NUM_CPU_IRQS 64 - -#ifndef __ASSEMBLY__ -/* Initialize chip interrupts. */ -extern void anna_init_irqs (void); -#endif - - -/* Anna UART details (basically the same as the V850E/MA1, but 2 channels). */ -#define V850E_UART_NUM_CHANNELS 2 -#define V850E_UART_BASE_FREQ (SYS_CLOCK_FREQ / 2) -#define V850E_UART_CHIP_NAME "V850E2/NA85E2A" - -/* This is the UART channel that's actually connected on the board. */ -#define V850E_UART_CONSOLE_CHANNEL 1 - -/* This is a function that gets called before configuring the UART. */ -#define V850E_UART_PRE_CONFIGURE anna_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void anna_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - -/* This board supports RTS/CTS for the on-chip UART, but only for channel 1. */ - -/* CTS for UART channel 1 is pin P37 (bit 7 of port 3). */ -#define V850E_UART_CTS(chan) ((chan) == 1 ? !(ANNA_PORT_IO(3) & 0x80) : 1) -/* RTS for UART channel 1 is pin P07 (bit 7 of port 0). */ -#define V850E_UART_SET_RTS(chan, val) \ - do { \ - if (chan == 1) { \ - unsigned old = ANNA_PORT_IO(0); \ - if (val) \ - ANNA_PORT_IO(0) = old & ~0x80; \ - else \ - ANNA_PORT_IO(0) = old | 0x80; \ - } \ - } while (0) - - -/* Timer C details. */ -#define V850E_TIMER_C_BASE_ADDR 0xFFFFF600 - -/* Timer D details (the Anna actually has 5 of these; should change later). */ -#define V850E_TIMER_D_BASE_ADDR 0xFFFFF540 -#define V850E_TIMER_D_TMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x0) -#define V850E_TIMER_D_CMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x2) -#define V850E_TIMER_D_TMCD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x4) - -#define V850E_TIMER_D_BASE_FREQ SYS_CLOCK_FREQ -#define V850E_TIMER_D_TMCD_CS_MIN 1 /* min 2^1 divider */ - - -#endif /* __V850_ANNA_H__ */ diff --git a/include/asm-v850/as85ep1.h b/include/asm-v850/as85ep1.h deleted file mode 100644 index 5a5ca9073d09..000000000000 --- a/include/asm-v850/as85ep1.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * include/asm-v850/as85ep1.h -- AS85EP1 evaluation CPU chip/board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_AS85EP1_H__ -#define __V850_AS85EP1_H__ - -#include - - -#define CPU_MODEL "as85ep1" -#define CPU_MODEL_LONG "NEC V850E/AS85EP1" -#define PLATFORM "AS85EP1" -#define PLATFORM_LONG "NEC V850E/AS85EP1 evaluation board" - -#define CPU_CLOCK_FREQ 96000000 /* 96MHz */ -#define SYS_CLOCK_FREQ CPU_CLOCK_FREQ - - -/* 1MB of static RAM. */ -#define SRAM_ADDR 0x00400000 -#define SRAM_SIZE 0x00100000 /* 1MB */ -/* About 58MB of DRAM. This can actually be at one of two positions, - determined by jump JP3; we have to use the first position because the - second is partially out of processor instruction addressing range - (though in the second position there's actually 64MB available). */ -#define SDRAM_ADDR 0x00600000 -#define SDRAM_SIZE 0x039F8000 /* approx 58MB */ - -/* For */ -#define PAGE_OFFSET SRAM_ADDR - -/* We use on-chip RAM, for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. The AS85EP1 chip - 16K of internal RAM located slightly before I/O space. */ -#define R0_RAM_ADDR 0xFFFF8000 - - -/* AS85EP1 specific control registers. */ -#define AS85EP1_CSC_ADDR(n) (0xFFFFF060 + (n) * 2) -#define AS85EP1_CSC(n) (*(volatile u16 *)AS85EP1_CSC_ADDR(n)) -#define AS85EP1_BSC_ADDR 0xFFFFF066 -#define AS85EP1_BSC (*(volatile u16 *)AS85EP1_BSC_ADDR) -#define AS85EP1_BCT_ADDR(n) (0xFFFFF480 + (n) * 2) -#define AS85EP1_BCT(n) (*(volatile u16 *)AS85EP1_BCT_ADDR(n)) -#define AS85EP1_DWC_ADDR(n) (0xFFFFF484 + (n) * 2) -#define AS85EP1_DWC(n) (*(volatile u16 *)AS85EP1_DWC_ADDR(n)) -#define AS85EP1_BCC_ADDR 0xFFFFF488 -#define AS85EP1_BCC (*(volatile u16 *)AS85EP1_BCC_ADDR) -#define AS85EP1_ASC_ADDR 0xFFFFF48A -#define AS85EP1_ASC (*(volatile u16 *)AS85EP1_ASC_ADDR) -#define AS85EP1_BCP_ADDR 0xFFFFF48C -#define AS85EP1_BCP (*(volatile u16 *)AS85EP1_BCP_ADDR) -#define AS85EP1_LBS_ADDR 0xFFFFF48E -#define AS85EP1_LBS (*(volatile u16 *)AS85EP1_LBS_ADDR) -#define AS85EP1_BMC_ADDR 0xFFFFF498 -#define AS85EP1_BMC (*(volatile u16 *)AS85EP1_BMC_ADDR) -#define AS85EP1_PRC_ADDR 0xFFFFF49A -#define AS85EP1_PRC (*(volatile u16 *)AS85EP1_PRC_ADDR) -#define AS85EP1_SCR_ADDR(n) (0xFFFFF4A0 + (n) * 4) -#define AS85EP1_SCR(n) (*(volatile u16 *)AS85EP1_SCR_ADDR(n)) -#define AS85EP1_RFS_ADDR(n) (0xFFFFF4A2 + (n) * 4) -#define AS85EP1_RFS(n) (*(volatile u16 *)AS85EP1_RFS_ADDR(n)) -#define AS85EP1_IRAMM_ADDR 0xFFFFF80A -#define AS85EP1_IRAMM (*(volatile u8 *)AS85EP1_IRAMM_ADDR) - - - -/* I/O port P0-P13. */ -/* Direct I/O. Bits 0-7 are pins Pn0-Pn7. */ -#define AS85EP1_PORT_IO_ADDR(n) (0xFFFFF400 + (n) * 2) -#define AS85EP1_PORT_IO(n) (*(volatile u8 *)AS85EP1_PORT_IO_ADDR(n)) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define AS85EP1_PORT_PM_ADDR(n) (0xFFFFF420 + (n) * 2) -#define AS85EP1_PORT_PM(n) (*(volatile u8 *)AS85EP1_PORT_PM_ADDR(n)) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define AS85EP1_PORT_PMC_ADDR(n) (0xFFFFF440 + (n) * 2) -#define AS85EP1_PORT_PMC(n) (*(volatile u8 *)AS85EP1_PORT_PMC_ADDR(n)) - - -/* Hardware-specific interrupt numbers (in the kernel IRQ namespace). */ -#define IRQ_INTCCC(n) (0x0C + (n)) -#define IRQ_INTCCC_NUM 8 -#define IRQ_INTCMD(n) (0x14 + (n)) /* interval timer interrupts 0-5 */ -#define IRQ_INTCMD_NUM 6 -#define IRQ_INTSRE(n) (0x1E + (n)*3) /* UART 0-1 reception error */ -#define IRQ_INTSRE_NUM 2 -#define IRQ_INTSR(n) (0x1F + (n)*3) /* UART 0-1 reception completion */ -#define IRQ_INTSR_NUM 2 -#define IRQ_INTST(n) (0x20 + (n)*3) /* UART 0-1 transmission completion */ -#define IRQ_INTST_NUM 2 - -#define NUM_CPU_IRQS 64 - -#ifndef __ASSEMBLY__ -/* Initialize chip interrupts. */ -extern void as85ep1_init_irqs (void); -#endif - - -/* AS85EP1 UART details (basically the same as the V850E/MA1, but 2 channels). */ -#define V850E_UART_NUM_CHANNELS 2 -#define V850E_UART_BASE_FREQ (SYS_CLOCK_FREQ / 4) -#define V850E_UART_CHIP_NAME "V850E/NA85E" - -/* This is a function that gets called before configuring the UART. */ -#define V850E_UART_PRE_CONFIGURE as85ep1_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void as85ep1_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - -/* This board supports RTS/CTS for the on-chip UART, but only for channel 1. */ - -/* CTS for UART channel 1 is pin P54 (bit 4 of port 5). */ -#define V850E_UART_CTS(chan) ((chan) == 1 ? !(AS85EP1_PORT_IO(5) & 0x10) : 1) -/* RTS for UART channel 1 is pin P53 (bit 3 of port 5). */ -#define V850E_UART_SET_RTS(chan, val) \ - do { \ - if (chan == 1) { \ - unsigned old = AS85EP1_PORT_IO(5); \ - if (val) \ - AS85EP1_PORT_IO(5) = old & ~0x8; \ - else \ - AS85EP1_PORT_IO(5) = old | 0x8; \ - } \ - } while (0) - - -/* Timer C details. */ -#define V850E_TIMER_C_BASE_ADDR 0xFFFFF600 - -/* Timer D details (the AS85EP1 actually has 5 of these; should change later). */ -#define V850E_TIMER_D_BASE_ADDR 0xFFFFF540 -#define V850E_TIMER_D_TMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x0) -#define V850E_TIMER_D_CMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x2) -#define V850E_TIMER_D_TMCD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x4) - -#define V850E_TIMER_D_BASE_FREQ SYS_CLOCK_FREQ -#define V850E_TIMER_D_TMCD_CS_MIN 2 /* min 2^2 divider */ - - -#endif /* __V850_AS85EP1_H__ */ diff --git a/include/asm-v850/asm.h b/include/asm-v850/asm.h deleted file mode 100644 index bf1e785a5dde..000000000000 --- a/include/asm-v850/asm.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * include/asm-v850/asm.h -- Macros for writing assembly code - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#define G_ENTRY(name) \ - .balign 4; \ - .globl name; \ - .type name,@function; \ - name -#define G_DATA(name) \ - .globl name; \ - .type name,@object; \ - name -#define END(name) \ - .size name,.-name - -#define L_ENTRY(name) \ - .balign 4; \ - .type name,@function; \ - name -#define L_DATA(name) \ - .type name,@object; \ - name diff --git a/include/asm-v850/atomic.h b/include/asm-v850/atomic.h deleted file mode 100644 index e4e57de08f73..000000000000 --- a/include/asm-v850/atomic.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * include/asm-v850/atomic.h -- Atomic operations - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_ATOMIC_H__ -#define __V850_ATOMIC_H__ - - -#include - -#ifdef CONFIG_SMP -#error SMP not supported -#endif - -typedef struct { int counter; } atomic_t; - -#define ATOMIC_INIT(i) { (i) } - -#ifdef __KERNEL__ - -#define atomic_read(v) ((v)->counter) -#define atomic_set(v,i) (((v)->counter) = (i)) - -static inline int atomic_add_return (int i, volatile atomic_t *v) -{ - unsigned long flags; - int res; - - local_irq_save (flags); - res = v->counter + i; - v->counter = res; - local_irq_restore (flags); - - return res; -} - -static __inline__ int atomic_sub_return (int i, volatile atomic_t *v) -{ - unsigned long flags; - int res; - - local_irq_save (flags); - res = v->counter - i; - v->counter = res; - local_irq_restore (flags); - - return res; -} - -static __inline__ void atomic_clear_mask (unsigned long mask, unsigned long *addr) -{ - unsigned long flags; - - local_irq_save (flags); - *addr &= ~mask; - local_irq_restore (flags); -} - -#endif - -#define atomic_add(i, v) atomic_add_return ((i), (v)) -#define atomic_sub(i, v) atomic_sub_return ((i), (v)) - -#define atomic_dec_return(v) atomic_sub_return (1, (v)) -#define atomic_inc_return(v) atomic_add_return (1, (v)) -#define atomic_inc(v) atomic_inc_return (v) -#define atomic_dec(v) atomic_dec_return (v) - -/* - * atomic_inc_and_test - increment and test - * @v: pointer of type atomic_t - * - * Atomically increments @v by 1 - * and returns true if the result is zero, or false for all - * other cases. - */ -#define atomic_inc_and_test(v) (atomic_inc_return(v) == 0) - -#define atomic_sub_and_test(i,v) (atomic_sub_return ((i), (v)) == 0) -#define atomic_dec_and_test(v) (atomic_sub_return (1, (v)) == 0) -#define atomic_add_negative(i,v) (atomic_add_return ((i), (v)) < 0) - -static inline int atomic_cmpxchg(atomic_t *v, int old, int new) -{ - int ret; - unsigned long flags; - - local_irq_save(flags); - ret = v->counter; - if (likely(ret == old)) - v->counter = new; - local_irq_restore(flags); - - return ret; -} - -#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) - -static inline int atomic_add_unless(atomic_t *v, int a, int u) -{ - int ret; - unsigned long flags; - - local_irq_save(flags); - ret = v->counter; - if (ret != u) - v->counter += a; - local_irq_restore(flags); - - return ret != u; -} - -#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) - -/* Atomic operations are already serializing on ARM */ -#define smp_mb__before_atomic_dec() barrier() -#define smp_mb__after_atomic_dec() barrier() -#define smp_mb__before_atomic_inc() barrier() -#define smp_mb__after_atomic_inc() barrier() - -#include -#endif /* __V850_ATOMIC_H__ */ diff --git a/include/asm-v850/auxvec.h b/include/asm-v850/auxvec.h deleted file mode 100644 index f493232d0224..000000000000 --- a/include/asm-v850/auxvec.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __V850_AUXVEC_H__ -#define __V850_AUXVEC_H__ - -#endif /* __V850_AUXVEC_H__ */ diff --git a/include/asm-v850/bitops.h b/include/asm-v850/bitops.h deleted file mode 100644 index f82f5b4a56e0..000000000000 --- a/include/asm-v850/bitops.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * include/asm-v850/bitops.h -- Bit operations - * - * Copyright (C) 2001,02,03,04,05 NEC Electronics Corporation - * Copyright (C) 2001,02,03,04,05 Miles Bader - * Copyright (C) 1992 Linus Torvalds. - * - * 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 for more details. - */ - -#ifndef __V850_BITOPS_H__ -#define __V850_BITOPS_H__ - -#ifndef _LINUX_BITOPS_H -#error only can be included directly -#endif - -#include /* unlikely */ -#include /* swab32 */ -#include /* interrupt enable/disable */ - - -#ifdef __KERNEL__ - -#include - -/* - * The __ functions are not atomic - */ - -/* In the following constant-bit-op macros, a "g" constraint is used when - we really need an integer ("i" constraint). This is to avoid - warnings/errors from the compiler in the case where the associated - operand _isn't_ an integer, and shouldn't produce bogus assembly because - use of that form is protected by a guard statement that checks for - constants, and should otherwise be removed by the optimizer. This - _usually_ works -- however, __builtin_constant_p returns true for a - variable with a known constant value too, and unfortunately gcc will - happily put the variable in a register and use the register for the "g" - constraint'd asm operand. To avoid the latter problem, we add a - constant offset to the operand and subtract it back in the asm code; - forcing gcc to do arithmetic on the value is usually enough to get it - to use a real constant value. This is horrible, and ultimately - unreliable too, but it seems to work for now (hopefully gcc will offer - us more control in the future, so we can do a better job). */ - -#define __const_bit_op(op, nr, addr) \ - ({ __asm__ (op " (%0 - 0x123), %1" \ - :: "g" (((nr) & 0x7) + 0x123), \ - "m" (*((char *)(addr) + ((nr) >> 3))) \ - : "memory"); }) -#define __var_bit_op(op, nr, addr) \ - ({ int __nr = (nr); \ - __asm__ (op " %0, [%1]" \ - :: "r" (__nr & 0x7), \ - "r" ((char *)(addr) + (__nr >> 3)) \ - : "memory"); }) -#define __bit_op(op, nr, addr) \ - ((__builtin_constant_p (nr) && (unsigned)(nr) <= 0x7FFFF) \ - ? __const_bit_op (op, nr, addr) \ - : __var_bit_op (op, nr, addr)) - -#define __set_bit(nr, addr) __bit_op ("set1", nr, addr) -#define __clear_bit(nr, addr) __bit_op ("clr1", nr, addr) -#define __change_bit(nr, addr) __bit_op ("not1", nr, addr) - -/* The bit instructions used by `non-atomic' variants are actually atomic. */ -#define set_bit __set_bit -#define clear_bit __clear_bit -#define change_bit __change_bit - - -#define __const_tns_bit_op(op, nr, addr) \ - ({ int __tns_res; \ - __asm__ __volatile__ ( \ - "tst1 (%1 - 0x123), %2; setf nz, %0; " op " (%1 - 0x123), %2" \ - : "=&r" (__tns_res) \ - : "g" (((nr) & 0x7) + 0x123), \ - "m" (*((char *)(addr) + ((nr) >> 3))) \ - : "memory"); \ - __tns_res; \ - }) -#define __var_tns_bit_op(op, nr, addr) \ - ({ int __nr = (nr); \ - int __tns_res; \ - __asm__ __volatile__ ( \ - "tst1 %1, [%2]; setf nz, %0; " op " %1, [%2]" \ - : "=&r" (__tns_res) \ - : "r" (__nr & 0x7), \ - "r" ((char *)(addr) + (__nr >> 3)) \ - : "memory"); \ - __tns_res; \ - }) -#define __tns_bit_op(op, nr, addr) \ - ((__builtin_constant_p (nr) && (unsigned)(nr) <= 0x7FFFF) \ - ? __const_tns_bit_op (op, nr, addr) \ - : __var_tns_bit_op (op, nr, addr)) -#define __tns_atomic_bit_op(op, nr, addr) \ - ({ int __tns_atomic_res, __tns_atomic_flags; \ - local_irq_save (__tns_atomic_flags); \ - __tns_atomic_res = __tns_bit_op (op, nr, addr); \ - local_irq_restore (__tns_atomic_flags); \ - __tns_atomic_res; \ - }) - -#define __test_and_set_bit(nr, addr) __tns_bit_op ("set1", nr, addr) -#define test_and_set_bit(nr, addr) __tns_atomic_bit_op ("set1", nr, addr) - -#define __test_and_clear_bit(nr, addr) __tns_bit_op ("clr1", nr, addr) -#define test_and_clear_bit(nr, addr) __tns_atomic_bit_op ("clr1", nr, addr) - -#define __test_and_change_bit(nr, addr) __tns_bit_op ("not1", nr, addr) -#define test_and_change_bit(nr, addr) __tns_atomic_bit_op ("not1", nr, addr) - - -#define __const_test_bit(nr, addr) \ - ({ int __test_bit_res; \ - __asm__ __volatile__ ("tst1 (%1 - 0x123), %2; setf nz, %0" \ - : "=r" (__test_bit_res) \ - : "g" (((nr) & 0x7) + 0x123), \ - "m" (*((const char *)(addr) + ((nr) >> 3)))); \ - __test_bit_res; \ - }) -static inline int __test_bit (int nr, const void *addr) -{ - int res; - __asm__ __volatile__ ("tst1 %1, [%2]; setf nz, %0" - : "=r" (res) - : "r" (nr & 0x7), "r" (addr + (nr >> 3))); - return res; -} -#define test_bit(nr,addr) \ - ((__builtin_constant_p (nr) && (unsigned)(nr) <= 0x7FFFF) \ - ? __const_test_bit ((nr), (addr)) \ - : __test_bit ((nr), (addr))) - - -/* clear_bit doesn't provide any barrier for the compiler. */ -#define smp_mb__before_clear_bit() barrier () -#define smp_mb__after_clear_bit() barrier () - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) -#define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) - -#include - -#endif /* __KERNEL__ */ - -#endif /* __V850_BITOPS_H__ */ diff --git a/include/asm-v850/bug.h b/include/asm-v850/bug.h deleted file mode 100644 index b0ed2d35f3e8..000000000000 --- a/include/asm-v850/bug.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * include/asm-v850/bug.h -- Bug reporting - * - * Copyright (C) 2003 NEC Electronics Corporation - * Copyright (C) 2003 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_BUG_H__ -#define __V850_BUG_H__ - -#ifdef CONFIG_BUG -extern void __bug (void) __attribute__ ((noreturn)); -#define BUG() __bug() -#define HAVE_ARCH_BUG -#endif - -#include - -#endif /* __V850_BUG_H__ */ diff --git a/include/asm-v850/bugs.h b/include/asm-v850/bugs.h deleted file mode 100644 index 71110a65c1d7..000000000000 --- a/include/asm-v850/bugs.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * include/asm-v850e/bugs.h - * - * Copyright (C) 1994 Linus Torvalds - */ - -/* - * This is included by init/main.c to check for architecture-dependent bugs. - * - * Needs: - * void check_bugs(void); - */ - -static void check_bugs(void) -{ -} diff --git a/include/asm-v850/byteorder.h b/include/asm-v850/byteorder.h deleted file mode 100644 index a6f07530050e..000000000000 --- a/include/asm-v850/byteorder.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * include/asm-v850/byteorder.h -- Endian id and conversion ops - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_BYTEORDER_H__ -#define __V850_BYTEORDER_H__ - -#include -#include - -#ifdef __GNUC__ - -static __inline__ __attribute_const__ __u32 ___arch__swab32 (__u32 word) -{ - __u32 res; - __asm__ ("bsw %1, %0" : "=r" (res) : "r" (word)); - return res; -} - -static __inline__ __attribute_const__ __u16 ___arch__swab16 (__u16 half_word) -{ - __u16 res; - __asm__ ("bsh %1, %0" : "=r" (res) : "r" (half_word)); - return res; -} - -#define __arch__swab32(x) ___arch__swab32(x) -#define __arch__swab16(x) ___arch__swab16(x) - -#if !defined(__STRICT_ANSI__) || defined(__KERNEL__) -# define __BYTEORDER_HAS_U64__ -# define __SWAB_64_THRU_32__ -#endif - -#endif /* __GNUC__ */ - -#include - -#endif /* __V850_BYTEORDER_H__ */ diff --git a/include/asm-v850/cache.h b/include/asm-v850/cache.h deleted file mode 100644 index 8832c7ea3242..000000000000 --- a/include/asm-v850/cache.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * include/asm-v850/cache.h -- Cache operations - * - * Copyright (C) 2001,05 NEC Corporation - * Copyright (C) 2001,05 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_CACHE_H__ -#define __V850_CACHE_H__ - -/* All cache operations are machine-dependent. */ -#include - -#ifndef L1_CACHE_BYTES -/* This processor has no cache, so just choose an arbitrary value. */ -#define L1_CACHE_BYTES 16 -#define L1_CACHE_SHIFT 4 -#endif - -#endif /* __V850_CACHE_H__ */ diff --git a/include/asm-v850/cacheflush.h b/include/asm-v850/cacheflush.h deleted file mode 100644 index 9ece05a202ef..000000000000 --- a/include/asm-v850/cacheflush.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * include/asm-v850/cacheflush.h - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_CACHEFLUSH_H__ -#define __V850_CACHEFLUSH_H__ - -/* Somebody depends on this; sigh... */ -#include - -#include - - -/* The following are all used by the kernel in ways that only affect - systems with MMUs, so we don't need them. */ -#define flush_cache_all() ((void)0) -#define flush_cache_mm(mm) ((void)0) -#define flush_cache_dup_mm(mm) ((void)0) -#define flush_cache_range(vma, start, end) ((void)0) -#define flush_cache_page(vma, vmaddr, pfn) ((void)0) -#define flush_dcache_page(page) ((void)0) -#define flush_dcache_mmap_lock(mapping) ((void)0) -#define flush_dcache_mmap_unlock(mapping) ((void)0) -#define flush_cache_vmap(start, end) ((void)0) -#define flush_cache_vunmap(start, end) ((void)0) - -#ifdef CONFIG_NO_CACHE - -/* Some systems have no cache at all, in which case we don't need these - either. */ -#define flush_icache() ((void)0) -#define flush_icache_range(start, end) ((void)0) -#define flush_icache_page(vma,pg) ((void)0) -#define flush_icache_user_range(vma,pg,adr,len) ((void)0) -#define flush_cache_sigtramp(vaddr) ((void)0) - -#else /* !CONFIG_NO_CACHE */ - -struct page; -struct mm_struct; -struct vm_area_struct; - -/* Otherwise, somebody had better define them. */ -extern void flush_icache (void); -extern void flush_icache_range (unsigned long start, unsigned long end); -extern void flush_icache_page (struct vm_area_struct *vma, struct page *page); -extern void flush_icache_user_range (struct vm_area_struct *vma, - struct page *page, - unsigned long adr, int len); -extern void flush_cache_sigtramp (unsigned long addr); - -#endif /* CONFIG_NO_CACHE */ - -#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ -do { memcpy(dst, src, len); \ - flush_icache_user_range(vma, page, vaddr, len); \ -} while (0) -#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ - memcpy(dst, src, len) - -#endif /* __V850_CACHEFLUSH_H__ */ diff --git a/include/asm-v850/checksum.h b/include/asm-v850/checksum.h deleted file mode 100644 index d1dddd938262..000000000000 --- a/include/asm-v850/checksum.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * include/asm-v850/checksum.h -- Checksum ops - * - * Copyright (C) 2001,2005 NEC Corporation - * Copyright (C) 2001,2005 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_CHECKSUM_H__ -#define __V850_CHECKSUM_H__ - -/* - * computes the checksum of a memory block at buff, length len, - * and adds in "sum" (32-bit) - * - * returns a 32-bit number suitable for feeding into itself - * or csum_tcpudp_magic - * - * this function must be called with even lengths, except - * for the last fragment, which may be odd - * - * it's best to have buff aligned on a 32-bit boundary - */ -extern __wsum csum_partial(const void *buff, int len, __wsum sum); - -/* - * the same as csum_partial, but copies from src while it - * checksums - * - * here even more important to align src and dst on a 32-bit (or even - * better 64-bit) boundary - */ -extern __wsum csum_partial_copy_nocheck(const void *src, - void *dst, int len, __wsum sum); - - -/* - * the same as csum_partial_copy, but copies from user space. - * - * here even more important to align src and dst on a 32-bit (or even - * better 64-bit) boundary - */ -extern __wsum csum_partial_copy_from_user (const void *src, - void *dst, - int len, __wsum sum, - int *csum_err); - -__sum16 ip_fast_csum(const void *iph, unsigned int ihl); - -/* - * Fold a partial checksum - */ -static inline __sum16 csum_fold (__wsum sum) -{ - unsigned int result; - /* - %0 %1 - hsw %1, %0 H L L H - add %1, %0 H L H+L+C H+L - */ - asm ("hsw %1, %0; add %1, %0" : "=&r" (result) : "r" (sum)); - return (__force __sum16)(~result >> 16); -} - - -/* - * computes the checksum of the TCP/UDP pseudo-header - * returns a 16-bit checksum, already complemented - */ -static inline __wsum -csum_tcpudp_nofold (__be32 saddr, __be32 daddr, - unsigned short len, - unsigned short proto, __wsum sum) -{ - int __carry; - __asm__ ("add %2, %0;" - "setf c, %1;" - "add %1, %0;" - "add %3, %0;" - "setf c, %1;" - "add %1, %0;" - "add %4, %0;" - "setf c, %1;" - "add %1, %0" - : "=&r" (sum), "=&r" (__carry) - : "r" (daddr), "r" (saddr), - "r" ((len + proto) << 8), - "0" (sum)); - return sum; -} - -static inline __sum16 -csum_tcpudp_magic (__be32 saddr, __be32 daddr, - unsigned short len, - unsigned short proto, __wsum sum) -{ - return csum_fold (csum_tcpudp_nofold (saddr, daddr, len, proto, sum)); -} - -/* - * this routine is used for miscellaneous IP-like checksums, mainly - * in icmp.c - */ -extern __sum16 ip_compute_csum(const void *buff, int len); - - -#endif /* __V850_CHECKSUM_H__ */ diff --git a/include/asm-v850/clinkage.h b/include/asm-v850/clinkage.h deleted file mode 100644 index c389691d6f86..000000000000 --- a/include/asm-v850/clinkage.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * include/asm-v850/clinkage.h -- Macros to reflect C symbol-naming conventions - * - * Copyright (C) 2001,02 NEC Corporatione - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_CLINKAGE_H__ -#define __V850_CLINKAGE_H__ - -#include -#include - -#define C_SYMBOL_NAME(name) macrology_paste(_, name) -#define C_SYMBOL_STRING(name) macrology_stringify(C_SYMBOL_NAME(name)) -#define C_ENTRY(name) G_ENTRY(C_SYMBOL_NAME(name)) -#define C_DATA(name) G_DATA(C_SYMBOL_NAME(name)) -#define C_END(name) END(C_SYMBOL_NAME(name)) - -#endif /* __V850_CLINKAGE_H__ */ diff --git a/include/asm-v850/cputime.h b/include/asm-v850/cputime.h deleted file mode 100644 index 7c799c33b8a9..000000000000 --- a/include/asm-v850/cputime.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_CPUTIME_H -#define __V850_CPUTIME_H - -#include - -#endif /* __V850_CPUTIME_H */ diff --git a/include/asm-v850/current.h b/include/asm-v850/current.h deleted file mode 100644 index 30aae5673770..000000000000 --- a/include/asm-v850/current.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * include/asm-v850/current.h -- Current task - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_CURRENT_H__ -#define __V850_CURRENT_H__ - -#ifndef __ASSEMBLY__ /* is not asm-safe. */ -#include -#endif - -#include - - -/* Register used to hold the current task pointer while in the kernel. - Any `call clobbered' register without a special meaning should be OK, - but check asm/v850/kernel/entry.S to be sure. */ -#define CURRENT_TASK_REGNUM 16 -#define CURRENT_TASK macrology_paste (r, CURRENT_TASK_REGNUM) - - -#ifdef __ASSEMBLY__ - -/* Put a pointer to the current task structure into REG. */ -#define GET_CURRENT_TASK(reg) \ - GET_CURRENT_THREAD(reg); \ - ld.w TI_TASK[reg], reg - -#else /* !__ASSEMBLY__ */ - -/* A pointer to the current task. */ -register struct task_struct *current \ - __asm__ (macrology_stringify (CURRENT_TASK)); - -#endif /* __ASSEMBLY__ */ - - -#endif /* _V850_CURRENT_H */ diff --git a/include/asm-v850/delay.h b/include/asm-v850/delay.h deleted file mode 100644 index 6d028e6b2354..000000000000 --- a/include/asm-v850/delay.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * include/asm-v850/delay.h -- Delay routines, using a pre-computed - * "loops_per_second" value - * - * Copyright (C) 2001,03 NEC Corporation - * Copyright (C) 2001,03 Miles Bader - * Copyright (C) 1994 Hamish Macdonald - * - * 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 for more details. - */ - -#ifndef __V850_DELAY_H__ -#define __V850_DELAY_H__ - -#include - -static inline void __delay(unsigned long loops) -{ - if (loops) - __asm__ __volatile__ ("1: add -1, %0; bnz 1b" - : "=r" (loops) : "0" (loops)); -} - -/* - * Use only for very small delays ( < 1 msec). Should probably use a - * lookup table, really, as the multiplications take much too long with - * short delays. This is a "reasonable" implementation, though (and the - * first constant multiplications gets optimized away if the delay is - * a constant) - */ - -extern unsigned long loops_per_jiffy; - -static inline void udelay(unsigned long usecs) -{ - register unsigned long full_loops, part_loops; - - full_loops = ((usecs * HZ) / 1000000) * loops_per_jiffy; - usecs %= (1000000 / HZ); - part_loops = (usecs * HZ * loops_per_jiffy) / 1000000; - - __delay(full_loops + part_loops); -} - -#endif /* __V850_DELAY_H__ */ diff --git a/include/asm-v850/device.h b/include/asm-v850/device.h deleted file mode 100644 index d8f9872b0e2d..000000000000 --- a/include/asm-v850/device.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Arch specific extensions to struct device - * - * This file is released under the GPLv2 - */ -#include - diff --git a/include/asm-v850/div64.h b/include/asm-v850/div64.h deleted file mode 100644 index 6cd978cefb28..000000000000 --- a/include/asm-v850/div64.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-v850/dma-mapping.h b/include/asm-v850/dma-mapping.h deleted file mode 100644 index 1cc42c603a1b..000000000000 --- a/include/asm-v850/dma-mapping.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __V850_DMA_MAPPING_H__ -#define __V850_DMA_MAPPING_H__ - - -#ifdef CONFIG_PCI -#include -#else -#include -#endif - -#endif /* __V850_DMA_MAPPING_H__ */ diff --git a/include/asm-v850/dma.h b/include/asm-v850/dma.h deleted file mode 100644 index 2369849e2d0a..000000000000 --- a/include/asm-v850/dma.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __V850_DMA_H__ -#define __V850_DMA_H__ - -/* What should this be? */ -#define MAX_DMA_ADDRESS 0xFFFFFFFF - -/* reserve a DMA channel */ -extern int request_dma (unsigned int dmanr, const char * device_id); -/* release it again */ -extern void free_dma (unsigned int dmanr); - -#ifdef CONFIG_PCI -extern int isa_dma_bridge_buggy; -#else -#define isa_dma_bridge_buggy (0) -#endif - -#endif /* __V850_DMA_H__ */ diff --git a/include/asm-v850/elf.h b/include/asm-v850/elf.h deleted file mode 100644 index 28f5b176ff1a..000000000000 --- a/include/asm-v850/elf.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef __V850_ELF_H__ -#define __V850_ELF_H__ - -/* - * ELF register definitions.. - */ - -#include -#include -#include - -typedef unsigned long elf_greg_t; - -#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t)) -typedef elf_greg_t elf_gregset_t[ELF_NGREG]; - -typedef struct user_fpu_struct elf_fpregset_t; - -/* - * This is used to ensure we don't load something for the wrong architecture. - */ -#define elf_check_arch(x) \ - ((x)->e_machine == EM_V850 || (x)->e_machine == EM_CYGNUS_V850) - - -/* v850 relocation types. */ -#define R_V850_NONE 0 -#define R_V850_9_PCREL 1 -#define R_V850_22_PCREL 2 -#define R_V850_HI16_S 3 -#define R_V850_HI16 4 -#define R_V850_LO16 5 -#define R_V850_32 6 -#define R_V850_16 7 -#define R_V850_8 8 -#define R_V850_SDA_16_16_OFFSET 9 /* For ld.b, st.b, set1, clr1, - not1, tst1, movea, movhi */ -#define R_V850_SDA_15_16_OFFSET 10 /* For ld.w, ld.h, ld.hu, st.w, st.h */ -#define R_V850_ZDA_16_16_OFFSET 11 /* For ld.b, st.b, set1, clr1, - not1, tst1, movea, movhi */ -#define R_V850_ZDA_15_16_OFFSET 12 /* For ld.w, ld.h, ld.hu, st.w, st.h */ -#define R_V850_TDA_6_8_OFFSET 13 /* For sst.w, sld.w */ -#define R_V850_TDA_7_8_OFFSET 14 /* For sst.h, sld.h */ -#define R_V850_TDA_7_7_OFFSET 15 /* For sst.b, sld.b */ -#define R_V850_TDA_16_16_OFFSET 16 /* For set1, clr1, not1, tst1, - movea, movhi */ -#define R_V850_NUM 17 - - -/* - * These are used to set parameters in the core dumps. - */ -#define ELF_CLASS ELFCLASS32 -#ifdef __LITTLE_ENDIAN__ -#define ELF_DATA ELFDATA2LSB -#else -#define ELF_DATA ELFDATA2MSB -#endif -#define ELF_ARCH EM_V850 - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - - -#define ELF_CORE_COPY_REGS(_dest,_regs) \ - memcpy((char *) &_dest, (char *) _regs, \ - sizeof(struct pt_regs)); - -/* This yields a mask that user programs can use to figure out what - instruction set this CPU supports. This could be done in user space, - but it's not easy, and we've already done it here. */ - -#define ELF_HWCAP (0) - -/* This yields a string that ld.so will use to load implementation - specific libraries for optimization. This is more specific in - intent than poking at uname or /proc/cpuinfo. - - For the moment, we have only optimizations for the Intel generations, - but that could change... */ - -#define ELF_PLATFORM (NULL) - -#define ELF_PLAT_INIT(_r, load_addr) \ - do { \ - _r->gpr[0] = _r->gpr[1] = _r->gpr[2] = _r->gpr[3] = \ - _r->gpr[4] = _r->gpr[5] = _r->gpr[6] = _r->gpr[7] = \ - _r->gpr[8] = _r->gpr[9] = _r->gpr[10] = _r->gpr[11] = \ - _r->gpr[12] = _r->gpr[13] = _r->gpr[14] = _r->gpr[15] = \ - _r->gpr[16] = _r->gpr[17] = _r->gpr[18] = _r->gpr[19] = \ - _r->gpr[20] = _r->gpr[21] = _r->gpr[22] = _r->gpr[23] = \ - _r->gpr[24] = _r->gpr[25] = _r->gpr[26] = _r->gpr[27] = \ - _r->gpr[28] = _r->gpr[29] = _r->gpr[30] = _r->gpr[31] = \ - 0; \ - } while (0) - -#define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX_32BIT) - -#endif /* __V850_ELF_H__ */ diff --git a/include/asm-v850/emergency-restart.h b/include/asm-v850/emergency-restart.h deleted file mode 100644 index 108d8c48e42e..000000000000 --- a/include/asm-v850/emergency-restart.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_EMERGENCY_RESTART_H -#define _ASM_EMERGENCY_RESTART_H - -#include - -#endif /* _ASM_EMERGENCY_RESTART_H */ diff --git a/include/asm-v850/entry.h b/include/asm-v850/entry.h deleted file mode 100644 index d9df8ac48584..000000000000 --- a/include/asm-v850/entry.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * include/asm-v850/entry.h -- Definitions used by low-level trap handlers - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_ENTRY_H__ -#define __V850_ENTRY_H__ - - -#include -#include - - -/* These are special variables using by the kernel trap/interrupt code - to save registers in, at a time when there are no spare registers we - can use to do so, and we can't depend on the value of the stack - pointer. This means that they must be within a signed 16-bit - displacement of 0x00000000. */ - -#define KERNEL_VAR_SPACE_ADDR R0_RAM_ADDR - -#ifdef __ASSEMBLY__ -#define KERNEL_VAR(addr) addr[r0] -#else -#define KERNEL_VAR(addr) (*(volatile unsigned long *)(addr)) -#endif - -/* Kernel stack pointer, 4 bytes. */ -#define KSP_ADDR (KERNEL_VAR_SPACE_ADDR + 0) -#define KSP KERNEL_VAR (KSP_ADDR) -/* 1 if in kernel-mode, 0 if in user mode, 1 byte. */ -#define KM_ADDR (KERNEL_VAR_SPACE_ADDR + 4) -#define KM KERNEL_VAR (KM_ADDR) -/* Temporary storage for interrupt handlers, 4 bytes. */ -#define INT_SCRATCH_ADDR (KERNEL_VAR_SPACE_ADDR + 8) -#define INT_SCRATCH KERNEL_VAR (INT_SCRATCH_ADDR) -/* Where the stack-pointer is saved when jumping to various sorts of - interrupt handlers. ENTRY_SP is used by everything except NMIs, - which have their own location. Higher-priority NMIs can clobber the - value written by a lower priority NMI, since they can't be disabled, - but that's OK, because only NMI0 (the lowest-priority one) is allowed - to return. */ -#define ENTRY_SP_ADDR (KERNEL_VAR_SPACE_ADDR + 12) -#define ENTRY_SP KERNEL_VAR (ENTRY_SP_ADDR) -#define NMI_ENTRY_SP_ADDR (KERNEL_VAR_SPACE_ADDR + 16) -#define NMI_ENTRY_SP KERNEL_VAR (NMI_ENTRY_SP_ADDR) - -#ifdef CONFIG_RESET_GUARD -/* Used to detect unexpected resets (since the v850 has no MMU, any call - through a null pointer will jump to the reset vector). We detect - such resets by checking for a magic value, RESET_GUARD_ACTIVE, in - this location. Properly resetting the machine stores zero there, so - it shouldn't trigger the guard; the power-on value is uncertain, but - it's unlikely to be RESET_GUARD_ACTIVE. */ -#define RESET_GUARD_ADDR (KERNEL_VAR_SPACE_ADDR + 28) -#define RESET_GUARD KERNEL_VAR (RESET_GUARD_ADDR) -#define RESET_GUARD_ACTIVE 0xFAB4BEEF -#endif /* CONFIG_RESET_GUARD */ - -#ifdef CONFIG_V850E_HIGHRES_TIMER -#define HIGHRES_TIMER_SLOW_TICKS_ADDR (KERNEL_VAR_SPACE_ADDR + 32) -#define HIGHRES_TIMER_SLOW_TICKS KERNEL_VAR (HIGHRES_TIMER_SLOW_TICKS_ADDR) -#endif /* CONFIG_V850E_HIGHRES_TIMER */ - -#ifndef __ASSEMBLY__ - -#ifdef CONFIG_RESET_GUARD -/* Turn off reset guard, so that resetting the machine works normally. - This should be called in the various machine_halt, etc., functions. */ -static inline void disable_reset_guard (void) -{ - RESET_GUARD = 0; -} -#endif /* CONFIG_RESET_GUARD */ - -#endif /* !__ASSEMBLY__ */ - - -/* A `state save frame' is a struct pt_regs preceded by some extra space - suitable for a function call stack frame. */ - -/* Amount of room on the stack reserved for arguments and to satisfy the - C calling conventions, in addition to the space used by the struct - pt_regs that actually holds saved values. */ -#define STATE_SAVE_ARG_SPACE (6*4) /* Up to six arguments. */ - - -#ifdef __ASSEMBLY__ - -/* The size of a state save frame. */ -#define STATE_SAVE_SIZE (PT_SIZE + STATE_SAVE_ARG_SPACE) - -#else /* !__ASSEMBLY__ */ - -/* The size of a state save frame. */ -#define STATE_SAVE_SIZE (sizeof (struct pt_regs) + STATE_SAVE_ARG_SPACE) - -#endif /* __ASSEMBLY__ */ - - -/* Offset of the struct pt_regs in a state save frame. */ -#define STATE_SAVE_PT_OFFSET STATE_SAVE_ARG_SPACE - - -#endif /* __V850_ENTRY_H__ */ diff --git a/include/asm-v850/errno.h b/include/asm-v850/errno.h deleted file mode 100644 index 31c91df01205..000000000000 --- a/include/asm-v850/errno.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_ERRNO_H__ -#define __V850_ERRNO_H__ - -#include - -#endif /* __V850_ERRNO_H__ */ diff --git a/include/asm-v850/fb.h b/include/asm-v850/fb.h deleted file mode 100644 index c7df38030992..000000000000 --- a/include/asm-v850/fb.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _ASM_FB_H_ -#define _ASM_FB_H_ -#include - -#define fb_pgprotect(...) do {} while (0) - -static inline int fb_is_primary_device(struct fb_info *info) -{ - return 0; -} - -#endif /* _ASM_FB_H_ */ diff --git a/include/asm-v850/fcntl.h b/include/asm-v850/fcntl.h deleted file mode 100644 index 3af4d56776dd..000000000000 --- a/include/asm-v850/fcntl.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __V850_FCNTL_H__ -#define __V850_FCNTL_H__ - -#define O_DIRECTORY 040000 /* must be a directory */ -#define O_NOFOLLOW 0100000 /* don't follow links */ -#define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ -#define O_LARGEFILE 0400000 - -#include - -#endif /* __V850_FCNTL_H__ */ diff --git a/include/asm-v850/flat.h b/include/asm-v850/flat.h deleted file mode 100644 index 17f0ea566611..000000000000 --- a/include/asm-v850/flat.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * include/asm-v850/flat.h -- uClinux flat-format executables - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_FLAT_H__ -#define __V850_FLAT_H__ - -/* The amount by which a relocation can exceed the program image limits - without being regarded as an error. On the v850, the relocations of - some base-pointers can be offset by 0x8000 (to allow better usage of the - space offered by 16-bit signed offsets -- in most cases the offsets used - with such a base-pointer will be negative). */ - -#define flat_reloc_valid(reloc, size) ((reloc) <= (size + 0x8000)) - -#define flat_stack_align(sp) /* nothing needed */ -#define flat_argvp_envp_on_stack() 0 -#define flat_old_ram_flag(flags) (flags) -#define flat_set_persistent(relval, p) 0 - -/* We store the type of relocation in the top 4 bits of the `relval.' */ - -/* Convert a relocation entry into an address. */ -static inline unsigned long -flat_get_relocate_addr (unsigned long relval) -{ - return relval & 0x0fffffff; /* Mask out top 4-bits */ -} - -#define flat_v850_get_reloc_type(relval) ((relval) >> 28) - -#define FLAT_V850_R_32 0 /* Normal 32-bit reloc */ -#define FLAT_V850_R_HI16S_LO15 1 /* High 16-bits + signed 15-bit low field */ -#define FLAT_V850_R_HI16S_LO16 2 /* High 16-bits + signed 16-bit low field */ - -/* Extract the address to be relocated from the symbol reference at RP; - RELVAL is the raw relocation-table entry from which RP is derived. - For the v850, RP should always be half-word aligned. */ -static inline unsigned long flat_get_addr_from_rp (unsigned long *rp, - unsigned long relval, - unsigned long flags, - unsigned long *persistent) -{ - short *srp = (short *)rp; - - switch (flat_v850_get_reloc_type (relval)) - { - case FLAT_V850_R_32: - /* Simple 32-bit address. */ - return srp[0] | (srp[1] << 16); - - case FLAT_V850_R_HI16S_LO16: - /* The high and low halves of the address are in the 16 - bits at RP, and the 2nd word of the 32-bit instruction - following that, respectively. The low half is _signed_ - so we have to sign-extend it and add it to the upper - half instead of simply or-ing them together. - - Unlike most relocated address, this one is stored in - native (little-endian) byte-order to avoid problems with - trashing the low-order bit, so we have to convert to - network-byte-order before returning, as that's what the - caller expects. */ - return htonl ((srp[0] << 16) + srp[2]); - - case FLAT_V850_R_HI16S_LO15: - /* The high and low halves of the address are in the 16 - bits at RP, and the upper 15 bits of the 2nd word of the - 32-bit instruction following that, respectively. The - low half is _signed_ so we have to sign-extend it and - add it to the upper half instead of simply or-ing them - together. The lowest bit is always zero. - - Unlike most relocated address, this one is stored in - native (little-endian) byte-order to avoid problems with - trashing the low-order bit, so we have to convert to - network-byte-order before returning, as that's what the - caller expects. */ - return htonl ((srp[0] << 16) + (srp[2] & ~0x1)); - - default: - return ~0; /* bogus value */ - } -} - -/* Insert the address ADDR into the symbol reference at RP; - RELVAL is the raw relocation-table entry from which RP is derived. - For the v850, RP should always be half-word aligned. */ -static inline void flat_put_addr_at_rp (unsigned long *rp, unsigned long addr, - unsigned long relval) -{ - short *srp = (short *)rp; - - switch (flat_v850_get_reloc_type (relval)) { - case FLAT_V850_R_32: - /* Simple 32-bit address. */ - srp[0] = addr & 0xFFFF; - srp[1] = (addr >> 16); - break; - - case FLAT_V850_R_HI16S_LO16: - /* The high and low halves of the address are in the 16 - bits at RP, and the 2nd word of the 32-bit instruction - following that, respectively. The low half is _signed_ - so we must carry its sign bit to the upper half before - writing the upper half. */ - srp[0] = (addr >> 16) + ((addr >> 15) & 0x1); - srp[2] = addr & 0xFFFF; - break; - - case FLAT_V850_R_HI16S_LO15: - /* The high and low halves of the address are in the 16 - bits at RP, and the upper 15 bits of the 2nd word of the - 32-bit instruction following that, respectively. The - low half is _signed_ so we must carry its sign bit to - the upper half before writing the upper half. The - lowest bit we preserve from the existing instruction. */ - srp[0] = (addr >> 16) + ((addr >> 15) & 0x1); - srp[2] = (addr & 0xFFFE) | (srp[2] & 0x1); - break; - } -} - -#endif /* __V850_FLAT_H__ */ diff --git a/include/asm-v850/fpga85e2c.h b/include/asm-v850/fpga85e2c.h deleted file mode 100644 index 23aae666c718..000000000000 --- a/include/asm-v850/fpga85e2c.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * include/asm-v850/fpga85e2c.h -- Machine-dependent defs for - * FPGA implementation of V850E2/NA85E2C - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_FPGA85E2C_H__ -#define __V850_FPGA85E2C_H__ - -#include -#include - - -#define CPU_MODEL "v850e2/fpga85e2c" -#define CPU_MODEL_LONG "NEC V850E2/NA85E2C" -#define PLATFORM "fpga85e2c" -#define PLATFORM_LONG "NA85E2C FPGA implementation" - - -/* `external ram'. */ -#define ERAM_ADDR 0 -#define ERAM_SIZE 0x00100000 /* 1MB */ - - -/* FPGA specific control registers. */ - -/* Writing a non-zero value to FLGREG(0) will signal the controlling CPU - to stop execution. */ -#define FLGREG_ADDR(n) (0xFFE80100 + 2*(n)) -#define FLGREG(n) (*(volatile unsigned char *)FLGREG_ADDR (n)) -#define FLGREG_NUM 2 - -#define CSDEV_ADDR(n) (0xFFE80110 + 2*(n)) -#define CSDEV(n) (*(volatile unsigned char *)CSDEV_ADDR (n)) - - -/* Timer interrupts 0-3, interrupt at intervals from CLK/4096 to CLK/16384. */ -#define IRQ_RPU(n) (60 + (n)) -#define IRQ_RPU_NUM 4 - -/* For */ -#define NUM_CPU_IRQS 64 - - -/* General-purpose timer. */ -/* control/status register (can only be read/written via bit insns) */ -#define RPU_GTMC_ADDR 0xFFFFFB00 -#define RPU_GTMC (*(volatile unsigned char *)RPU_GTMC_ADDR) -#define RPU_GTMC_CE_BIT 7 /* clock enable (control) */ -#define RPU_GTMC_OV_BIT 6 /* overflow (status) */ -#define RPU_GTMC_CLK_BIT 1 /* 0 = .5 MHz CLK, 1 = 1 Mhz (control) */ -/* 32-bit count (8 least-significant bits are always zero). */ -#define RPU_GTM_ADDR 0xFFFFFB28 -#define RPU_GTM (*(volatile unsigned long *)RPU_GTMC_ADDR) - - -/* For */ -#define PAGE_OFFSET ERAM_ADDR /* minimum allocatable address */ - - -/* For */ -/* `R0 RAM', used for a few miscellaneous variables that must be accessible - using a load instruction relative to R0. The FPGA implementation - actually has no on-chip RAM, so we use part of main ram just after the - interrupt vectors. */ -#ifdef __ASSEMBLY__ -#define R0_RAM_ADDR lo(C_SYMBOL_NAME(_r0_ram)) -#else -extern char _r0_ram; -#define R0_RAM_ADDR ((unsigned long)&_r0_ram); -#endif - - -#endif /* __V850_FPGA85E2C_H__ */ diff --git a/include/asm-v850/futex.h b/include/asm-v850/futex.h deleted file mode 100644 index 6a332a9f099c..000000000000 --- a/include/asm-v850/futex.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_FUTEX_H -#define _ASM_FUTEX_H - -#include - -#endif diff --git a/include/asm-v850/gbus_int.h b/include/asm-v850/gbus_int.h deleted file mode 100644 index 0c4bce753c7e..000000000000 --- a/include/asm-v850/gbus_int.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * include/asm-v850/gbus_int.h -- Midas labs GBUS interrupt support - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_GBUS_INT_H__ -#define __V850_GBUS_INT_H__ - - -/* The GBUS interrupt interface has 32 interrupts shared among 4 - processor interrupts. The 32 GBUS interrupts are divided into two - sets of 16 each, for allocating among control registers, etc (there - are two of each control register, with bits 0-15 controlling an - interrupt each). */ - -/* The GBUS interrupts themselves. */ -#define IRQ_GBUS_INT(n) (GBUS_INT_BASE_IRQ + (n)) -#define IRQ_GBUS_INT_NUM 32 - -/* Control registers. */ -#define GBUS_INT_STATUS_ADDR(w) (GBUS_INT_BASE_ADDR + (w)*0x40) -#define GBUS_INT_STATUS(w) (*(volatile u16 *)GBUS_INT_STATUS_ADDR(w)) -#define GBUS_INT_CLEAR_ADDR(w) (GBUS_INT_BASE_ADDR + 0x10 + (w)*0x40) -#define GBUS_INT_CLEAR(w) (*(volatile u16 *)GBUS_INT_CLEAR_ADDR(w)) -#define GBUS_INT_EDGE_ADDR(w) (GBUS_INT_BASE_ADDR + 0x20 + (w)*0x40) -#define GBUS_INT_EDGE(w) (*(volatile u16 *)GBUS_INT_EDGE_ADDR(w)) -#define GBUS_INT_POLARITY_ADDR(w) (GBUS_INT_BASE_ADDR + 0x30 + (w)*0x40) -#define GBUS_INT_POLARITY(w) (*(volatile u16 *)GBUS_INT_POLARITY_ADDR(w)) -/* This allows enabling interrupt bits in word W for interrupt GINTn. */ -#define GBUS_INT_ENABLE_ADDR(w, n) \ - (GBUS_INT_BASE_ADDR + 0x100 + (w)*0x10 + (n)*0x20) -#define GBUS_INT_ENABLE(w, n) (*(volatile u16 *)GBUS_INT_ENABLE_ADDR(w, n)) - -/* Mapping between kernel interrupt numbers and hardware control regs/bits. */ -#define GBUS_INT_BITS_PER_WORD 16 -#define GBUS_INT_NUM_WORDS (IRQ_GBUS_INT_NUM / GBUS_INT_BITS_PER_WORD) -#define GBUS_INT_IRQ_WORD(irq) (((irq) - GBUS_INT_BASE_IRQ) >> 4) -#define GBUS_INT_IRQ_BIT(irq) (((irq) - GBUS_INT_BASE_IRQ) & 0xF) -#define GBUS_INT_IRQ_MASK(irq) (1 << GBUS_INT_IRQ_BIT(irq)) - - -/* Possible priorities for GBUS interrupts. */ -#define GBUS_INT_PRIORITY_HIGH 2 -#define GBUS_INT_PRIORITY_MEDIUM 4 -#define GBUS_INT_PRIORITY_LOW 6 - - -#ifndef __ASSEMBLY__ - -/* Enable interrupt handling for interrupt IRQ. */ -extern void gbus_int_enable_irq (unsigned irq); -/* Disable interrupt handling for interrupt IRQ. Note that any - interrupts received while disabled will be delivered once the - interrupt is enabled again, unless they are explicitly cleared using - `gbus_int_clear_pending_irq'. */ -extern void gbus_int_disable_irq (unsigned irq); -/* Return true if interrupt handling for interrupt IRQ is enabled. */ -extern int gbus_int_irq_enabled (unsigned irq); -/* Disable all GBUS irqs. */ -extern void gbus_int_disable_irqs (void); -/* Clear any pending interrupts for IRQ. */ -extern void gbus_int_clear_pending_irq (unsigned irq); -/* Return true if interrupt IRQ is pending (but disabled). */ -extern int gbus_int_irq_pending (unsigned irq); - - -struct gbus_int_irq_init { - const char *name; /* name of interrupt type */ - - /* Range of kernel irq numbers for this type: - BASE, BASE+INTERVAL, ..., BASE+INTERVAL*NUM */ - unsigned base, num, interval; - - unsigned priority; /* interrupt priority to assign */ -}; -struct hw_interrupt_type; /* fwd decl */ - -/* Initialize HW_IRQ_TYPES for GBUS irqs described in array - INITS (which is terminated by an entry with the name field == 0). */ -extern void gbus_int_init_irq_types (struct gbus_int_irq_init *inits, - struct hw_interrupt_type *hw_irq_types); - -/* Initialize GBUS interrupts. */ -extern void gbus_int_init_irqs (void); - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_GBUS_INT_H__ */ diff --git a/include/asm-v850/hardirq.h b/include/asm-v850/hardirq.h deleted file mode 100644 index 04e20127c5af..000000000000 --- a/include/asm-v850/hardirq.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef __V850_HARDIRQ_H__ -#define __V850_HARDIRQ_H__ - -#include -#include - -#include - -typedef struct { - unsigned int __softirq_pending; -} ____cacheline_aligned irq_cpustat_t; - -#include /* Standard mappings for irq_cpustat_t above */ - -#define HARDIRQ_BITS 8 - -/* - * The hardirq mask has to be large enough to have - * space for potentially all IRQ sources in the system - * nesting on a single CPU: - */ -#if (1 << HARDIRQ_BITS) < NR_IRQS -# error HARDIRQ_BITS is too low! -#endif - -void ack_bad_irq(unsigned int irq); - -#endif /* __V850_HARDIRQ_H__ */ diff --git a/include/asm-v850/highres_timer.h b/include/asm-v850/highres_timer.h deleted file mode 100644 index 486fb49ceab6..000000000000 --- a/include/asm-v850/highres_timer.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * include/asm-v850/highres_timer.h -- High resolution timing routines - * - * Copyright (C) 2001,03 NEC Electronics Corporation - * Copyright (C) 2001,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_HIGHRES_TIMER_H__ -#define __V850_HIGHRES_TIMER_H__ - -#ifndef __ASSEMBLY__ -#include -#endif - -#include - - -/* Frequency of the `slow ticks' (one tick each time the fast-tick - counter overflows). */ -#define HIGHRES_TIMER_SLOW_TICK_RATE 25 - -/* Which timer in the V850E `Timer D' we use. */ -#define HIGHRES_TIMER_TIMER_D_UNIT 3 - - -#ifndef __ASSEMBLY__ - -extern void highres_timer_start (void), highres_timer_stop (void); -extern void highres_timer_reset (void); -extern void highres_timer_read_ticks (u32 *slow_ticks, u32 *fast_ticks); -extern void highres_timer_ticks_to_timeval (u32 slow_ticks, u32 fast_ticks, - struct timeval *tv); -extern void highres_timer_read (struct timeval *tv); - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_HIGHRES_TIMER_H__ */ diff --git a/include/asm-v850/hw_irq.h b/include/asm-v850/hw_irq.h deleted file mode 100644 index 043e94bb6bd8..000000000000 --- a/include/asm-v850/hw_irq.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef __V850_HW_IRQ_H__ -#define __V850_HW_IRQ_H__ - -#endif /* __V850_HW_IRQ_H__ */ diff --git a/include/asm-v850/io.h b/include/asm-v850/io.h deleted file mode 100644 index cdad251fba9f..000000000000 --- a/include/asm-v850/io.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * include/asm-v850/io.h -- Misc I/O operations - * - * Copyright (C) 2001,02,03,04,05 NEC Electronics Corporation - * Copyright (C) 2001,02,03,04,05 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_IO_H__ -#define __V850_IO_H__ - -#define IO_SPACE_LIMIT 0xFFFFFFFF - -#define readb(addr) \ - ({ unsigned char __v = (*(volatile unsigned char *) (addr)); __v; }) -#define readw(addr) \ - ({ unsigned short __v = (*(volatile unsigned short *) (addr)); __v; }) -#define readl(addr) \ - ({ unsigned long __v = (*(volatile unsigned long *) (addr)); __v; }) - -#define readb_relaxed(a) readb(a) -#define readw_relaxed(a) readw(a) -#define readl_relaxed(a) readl(a) - -#define writeb(val, addr) \ - (void)((*(volatile unsigned char *) (addr)) = (val)) -#define writew(val, addr) \ - (void)((*(volatile unsigned short *) (addr)) = (val)) -#define writel(val, addr) \ - (void)((*(volatile unsigned int *) (addr)) = (val)) - -#define __raw_readb readb -#define __raw_readw readw -#define __raw_readl readl -#define __raw_writeb writeb -#define __raw_writew writew -#define __raw_writel writel - -#define inb(addr) readb (addr) -#define inw(addr) readw (addr) -#define inl(addr) readl (addr) -#define outb(x, addr) ((void) writeb (x, addr)) -#define outw(x, addr) ((void) writew (x, addr)) -#define outl(x, addr) ((void) writel (x, addr)) - -#define inb_p(port) inb((port)) -#define outb_p(val, port) outb((val), (port)) -#define inw_p(port) inw((port)) -#define outw_p(val, port) outw((val), (port)) -#define inl_p(port) inl((port)) -#define outl_p(val, port) outl((val), (port)) - -static inline void insb (unsigned long port, void *dst, unsigned long count) -{ - unsigned char *p = dst; - while (count--) - *p++ = inb (port); -} -static inline void insw (unsigned long port, void *dst, unsigned long count) -{ - unsigned short *p = dst; - while (count--) - *p++ = inw (port); -} -static inline void insl (unsigned long port, void *dst, unsigned long count) -{ - unsigned long *p = dst; - while (count--) - *p++ = inl (port); -} - -static inline void -outsb (unsigned long port, const void *src, unsigned long count) -{ - const unsigned char *p = src; - while (count--) - outb (*p++, port); -} -static inline void -outsw (unsigned long port, const void *src, unsigned long count) -{ - const unsigned short *p = src; - while (count--) - outw (*p++, port); -} -static inline void -outsl (unsigned long port, const void *src, unsigned long count) -{ - const unsigned long *p = src; - while (count--) - outl (*p++, port); -} - - -/* Some places try to pass in an loff_t for PHYSADDR (?!), so we cast it to - long before casting it to a pointer to avoid compiler warnings. */ -#define ioremap(physaddr, size) ((void __iomem *)(unsigned long)(physaddr)) -#define iounmap(addr) ((void)0) - -#define ioremap_nocache(physaddr, size) ioremap (physaddr, size) -#define ioremap_writethrough(physaddr, size) ioremap (physaddr, size) -#define ioremap_fullcache(physaddr, size) ioremap (physaddr, size) - -#define ioread8(addr) readb (addr) -#define ioread16(addr) readw (addr) -#define ioread32(addr) readl (addr) -#define iowrite8(val, addr) writeb (val, addr) -#define iowrite16(val, addr) writew (val, addr) -#define iowrite32(val, addr) writel (val, addr) - -#define mmiowb() - -#define page_to_phys(page) ((page - mem_map) << PAGE_SHIFT) -#if 0 -/* This is really stupid; don't define it. */ -#define page_to_bus(page) page_to_phys (page) -#endif - -/* Conversion between virtual and physical mappings. */ -#define phys_to_virt(addr) ((void *)__phys_to_virt (addr)) -#define virt_to_phys(addr) ((unsigned long)__virt_to_phys (addr)) - -#define memcpy_fromio(dst, src, len) memcpy (dst, (void *)src, len) -#define memcpy_toio(dst, src, len) memcpy ((void *)dst, src, len) - -/* - * Convert a physical pointer to a virtual kernel pointer for /dev/mem - * access - */ -#define xlate_dev_mem_ptr(p) __va(p) - -/* - * Convert a virtual cached pointer to an uncached pointer - */ -#define xlate_dev_kmem_ptr(p) p - -#endif /* __V850_IO_H__ */ diff --git a/include/asm-v850/ioctl.h b/include/asm-v850/ioctl.h deleted file mode 100644 index b279fe06dfe5..000000000000 --- a/include/asm-v850/ioctl.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-v850/ioctls.h b/include/asm-v850/ioctls.h deleted file mode 100644 index 5313abd5f388..000000000000 --- a/include/asm-v850/ioctls.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef __V850_IOCTLS_H__ -#define __V850_IOCTLS_H__ - -#include - -/* 0x54 is just a magic number to make these relatively unique ('T') */ - -#define TCGETS 0x5401 -#define TCSETS 0x5402 -#define TCSETSW 0x5403 -#define TCSETSF 0x5404 -#define TCGETA 0x5405 -#define TCSETA 0x5406 -#define TCSETAW 0x5407 -#define TCSETAF 0x5408 -#define TCSBRK 0x5409 -#define TCXONC 0x540A -#define TCFLSH 0x540B -#define TIOCEXCL 0x540C -#define TIOCNXCL 0x540D -#define TIOCSCTTY 0x540E -#define TIOCGPGRP 0x540F -#define TIOCSPGRP 0x5410 -#define TIOCOUTQ 0x5411 -#define TIOCSTI 0x5412 -#define TIOCGWINSZ 0x5413 -#define TIOCSWINSZ 0x5414 -#define TIOCMGET 0x5415 -#define TIOCMBIS 0x5416 -#define TIOCMBIC 0x5417 -#define TIOCMSET 0x5418 -#define TIOCGSOFTCAR 0x5419 -#define TIOCSSOFTCAR 0x541A -#define FIONREAD 0x541B -#define TIOCINQ FIONREAD -#define TIOCLINUX 0x541C -#define TIOCCONS 0x541D -#define TIOCGSERIAL 0x541E -#define TIOCSSERIAL 0x541F -#define TIOCPKT 0x5420 -#define FIONBIO 0x5421 -#define TIOCNOTTY 0x5422 -#define TIOCSETD 0x5423 -#define TIOCGETD 0x5424 -#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ -#define TIOCSBRK 0x5427 /* BSD compatibility */ -#define TIOCCBRK 0x5428 /* BSD compatibility */ -#define TIOCGSID 0x5429 /* Return the session ID of FD */ -#define TCGETS2 _IOR('T',0x2A, struct termios2) -#define TCSETS2 _IOW('T',0x2B, struct termios2) -#define TCSETSW2 _IOW('T',0x2C, struct termios2) -#define TCSETSF2 _IOW('T',0x2D, struct termios2) -#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ -#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ - -#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ -#define FIOCLEX 0x5451 -#define FIOASYNC 0x5452 -#define TIOCSERCONFIG 0x5453 -#define TIOCSERGWILD 0x5454 -#define TIOCSERSWILD 0x5455 -#define TIOCGLCKTRMIOS 0x5456 -#define TIOCSLCKTRMIOS 0x5457 -#define TIOCSERGSTRUCT 0x5458 /* For debugging only */ -#define TIOCSERGETLSR 0x5459 /* Get line status register */ -#define TIOCSERGETMULTI 0x545A /* Get multiport config */ -#define TIOCSERSETMULTI 0x545B /* Set multiport config */ - -#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ -#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ -#define FIOQSIZE 0x545E - -/* Used for packet mode */ -#define TIOCPKT_DATA 0 -#define TIOCPKT_FLUSHREAD 1 -#define TIOCPKT_FLUSHWRITE 2 -#define TIOCPKT_STOP 4 -#define TIOCPKT_START 8 -#define TIOCPKT_NOSTOP 16 -#define TIOCPKT_DOSTOP 32 - -#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ - -#endif /* __V850_IOCTLS_H__ */ diff --git a/include/asm-v850/ipcbuf.h b/include/asm-v850/ipcbuf.h deleted file mode 100644 index d8cbe9886d95..000000000000 --- a/include/asm-v850/ipcbuf.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __V850E_IPCBUF_H__ -#define __V850E_IPCBUF_H__ - -/* - * The user_ipc_perm structure for v850e architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 32-bit mode_t and seq - * - 2 miscellaneous 32-bit values - */ - -struct ipc64_perm -{ - __kernel_key_t key; - __kernel_uid32_t uid; - __kernel_gid32_t gid; - __kernel_uid32_t cuid; - __kernel_gid32_t cgid; - __kernel_mode_t mode; - unsigned short __pad1; - unsigned short seq; - unsigned short __pad2; - unsigned long __unused1; - unsigned long __unused2; -}; - -#endif /* __V850E_IPCBUF_H__ */ diff --git a/include/asm-v850/irq.h b/include/asm-v850/irq.h deleted file mode 100644 index 7d0d4cd1ce54..000000000000 --- a/include/asm-v850/irq.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * include/asm-v850/irq.h -- Machine interrupt handling - * - * Copyright (C) 2001,02,04 NEC Electronics Corporation - * Copyright (C) 2001,02,04 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_IRQ_H__ -#define __V850_IRQ_H__ - -#include - -/* Default NUM_MACH_IRQS. */ -#ifndef NUM_MACH_IRQS -#define NUM_MACH_IRQS NUM_CPU_IRQS -#endif - -/* NMIs have IRQ numbers from FIRST_NMI to FIRST_NMI+NUM_NMIS-1. */ -#define FIRST_NMI NUM_MACH_IRQS -#define IRQ_NMI(n) (FIRST_NMI + (n)) -/* v850 processors have 3 non-maskable interrupts. */ -#define NUM_NMIS 3 - -/* Includes both maskable and non-maskable irqs. */ -#define NR_IRQS (NUM_MACH_IRQS + NUM_NMIS) - - -#ifndef __ASSEMBLY__ - -struct pt_regs; -struct hw_interrupt_type; -struct irqaction; - -#define irq_canonicalize(irq) (irq) - -/* Initialize irq handling for IRQs. - BASE_IRQ, BASE_IRQ+INTERVAL, ..., BASE_IRQ+NUM*INTERVAL - to IRQ_TYPE. An IRQ_TYPE of 0 means to use a generic interrupt type. */ -extern void -init_irq_handlers (int base_irq, int num, int interval, - struct hw_interrupt_type *irq_type); - -/* Handle interrupt IRQ. REGS are the registers at the time of ther - interrupt. */ -extern unsigned int handle_irq (int irq, struct pt_regs *regs); - -#endif /* !__ASSEMBLY__ */ - -#endif /* __V850_IRQ_H__ */ diff --git a/include/asm-v850/irq_regs.h b/include/asm-v850/irq_regs.h deleted file mode 100644 index 3dd9c0b70270..000000000000 --- a/include/asm-v850/irq_regs.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-v850/kdebug.h b/include/asm-v850/kdebug.h deleted file mode 100644 index 6ece1b037665..000000000000 --- a/include/asm-v850/kdebug.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-v850/kmap_types.h b/include/asm-v850/kmap_types.h deleted file mode 100644 index 3288976b161f..000000000000 --- a/include/asm-v850/kmap_types.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __V850_KMAP_TYPES_H__ -#define __V850_KMAP_TYPES_H__ - -enum km_type { - KM_BOUNCE_READ, - KM_SKB_SUNRPC_DATA, - KM_SKB_DATA_SOFTIRQ, - KM_USER0, - KM_USER1, - KM_BIO_SRC_IRQ, - KM_BIO_DST_IRQ, - KM_PTE0, - KM_PTE1, - KM_IRQ0, - KM_IRQ1, - KM_TYPE_NR -}; - -#endif /* __V850_KMAP_TYPES_H__ */ diff --git a/include/asm-v850/kvm.h b/include/asm-v850/kvm.h deleted file mode 100644 index 3f729b79febc..000000000000 --- a/include/asm-v850/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_V850_H -#define __LINUX_KVM_V850_H - -/* v850 does not support KVM */ - -#endif diff --git a/include/asm-v850/linkage.h b/include/asm-v850/linkage.h deleted file mode 100644 index b6185d3cfe68..000000000000 --- a/include/asm-v850/linkage.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __ASM_LINKAGE_H -#define __ASM_LINKAGE_H - -#ifdef __ASSEMBLY__ -#include -#endif - -#endif diff --git a/include/asm-v850/local.h b/include/asm-v850/local.h deleted file mode 100644 index 705148abe276..000000000000 --- a/include/asm-v850/local.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_LOCAL_H__ -#define __V850_LOCAL_H__ - -#include - -#endif /* __V850_LOCAL_H__ */ diff --git a/include/asm-v850/ma.h b/include/asm-v850/ma.h deleted file mode 100644 index 89e66473a176..000000000000 --- a/include/asm-v850/ma.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * include/asm-v850/ma.h -- V850E/MA series of cpu chips - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_MA_H__ -#define __V850_MA_H__ - -/* The MA series uses the V850E cpu core. */ -#include - - -/* For */ -/* We use on-chip RAM, for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. The amount - varies between chip models, but there's always at least 4K, and it - should always start at FFFFC000. */ -#define R0_RAM_ADDR 0xFFFFC000 - - -/* MA series UART details. */ -#define V850E_UART_BASE_FREQ CPU_CLOCK_FREQ - -/* This is a function that gets called before configuring the UART. */ -#define V850E_UART_PRE_CONFIGURE ma_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void ma_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - - -/* MA series timer C details. */ -#define V850E_TIMER_C_BASE_ADDR 0xFFFFF600 - - -/* MA series timer D details. */ -#define V850E_TIMER_D_BASE_ADDR 0xFFFFF540 -#define V850E_TIMER_D_TMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x0) -#define V850E_TIMER_D_CMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x2) -#define V850E_TIMER_D_TMCD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x4) - -#define V850E_TIMER_D_BASE_FREQ CPU_CLOCK_FREQ - - -/* Port 0 */ -/* Direct I/O. Bits 0-7 are pins P00-P07. */ -#define MA_PORT0_IO_ADDR 0xFFFFF400 -#define MA_PORT0_IO (*(volatile u8 *)MA_PORT0_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define MA_PORT0_PM_ADDR 0xFFFFF420 -#define MA_PORT0_PM (*(volatile u8 *)MA_PORT0_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define MA_PORT0_PMC_ADDR 0xFFFFF440 -#define MA_PORT0_PMC (*(volatile u8 *)MA_PORT0_PMC_ADDR) -/* Port function control (for P04-P07, 0 = IRQ, 1 = DMARQ). */ -#define MA_PORT0_PFC_ADDR 0xFFFFF460 -#define MA_PORT0_PFC (*(volatile u8 *)MA_PORT0_PFC_ADDR) - -/* Port 1 */ -/* Direct I/O. Bits 0-3 are pins P10-P13. */ -#define MA_PORT1_IO_ADDR 0xFFFFF402 -#define MA_PORT1_IO (*(volatile u8 *)MA_PORT1_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define MA_PORT1_PM_ADDR 0xFFFFF420 -#define MA_PORT1_PM (*(volatile u8 *)MA_PORT1_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define MA_PORT1_PMC_ADDR 0xFFFFF442 -#define MA_PORT1_PMC (*(volatile u8 *)MA_PORT1_PMC_ADDR) - -/* Port 4 */ -/* Direct I/O. Bits 0-5 are pins P40-P45. */ -#define MA_PORT4_IO_ADDR 0xFFFFF408 -#define MA_PORT4_IO (*(volatile u8 *)MA_PORT4_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define MA_PORT4_PM_ADDR 0xFFFFF428 -#define MA_PORT4_PM (*(volatile u8 *)MA_PORT4_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define MA_PORT4_PMC_ADDR 0xFFFFF448 -#define MA_PORT4_PMC (*(volatile u8 *)MA_PORT4_PMC_ADDR) -/* Port function control (for serial interfaces, 0 = CSI, 1 = UART). */ -#define MA_PORT4_PFC_ADDR 0xFFFFF468 -#define MA_PORT4_PFC (*(volatile u8 *)MA_PORT4_PFC_ADDR) - - -#ifndef __ASSEMBLY__ - -/* Initialize MA chip interrupts. */ -extern void ma_init_irqs (void); - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_MA_H__ */ diff --git a/include/asm-v850/ma1.h b/include/asm-v850/ma1.h deleted file mode 100644 index ede1f1de2b7a..000000000000 --- a/include/asm-v850/ma1.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * include/asm-v850/ma1.h -- V850E/MA1 cpu chip - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_MA1_H__ -#define __V850_MA1_H__ - -/* Inherit more generic details from MA series. */ -#include - - -#define CPU_MODEL "v850e/ma1" -#define CPU_MODEL_LONG "NEC V850E/MA1" - - -/* Hardware-specific interrupt numbers (in the kernel IRQ namespace). */ -#define IRQ_INTOV(n) (n) /* 0-3 */ -#define IRQ_INTOV_NUM 4 -#define IRQ_INTP(n) (0x4 + (n)) /* Pnnn (pin) interrupts */ -#define IRQ_INTP_NUM 24 -#define IRQ_INTCMD(n) (0x1c + (n)) /* interval timer interrupts 0-3 */ -#define IRQ_INTCMD_NUM 4 -#define IRQ_INTDMA(n) (0x20 + (n)) /* DMA interrupts 0-3 */ -#define IRQ_INTDMA_NUM 4 -#define IRQ_INTCSI(n) (0x24 + (n)*4)/* CSI 0-2 transmit/receive completion */ -#define IRQ_INTCSI_NUM 3 -#define IRQ_INTSER(n) (0x25 + (n)*4) /* UART 0-2 reception error */ -#define IRQ_INTSER_NUM 3 -#define IRQ_INTSR(n) (0x26 + (n)*4) /* UART 0-2 reception completion */ -#define IRQ_INTSR_NUM 3 -#define IRQ_INTST(n) (0x27 + (n)*4) /* UART 0-2 transmission completion */ -#define IRQ_INTST_NUM 3 - -#define NUM_CPU_IRQS 0x30 - - -/* The MA1 has a UART with 3 channels. */ -#define V850E_UART_NUM_CHANNELS 3 - - -#endif /* __V850_MA1_H__ */ diff --git a/include/asm-v850/machdep.h b/include/asm-v850/machdep.h deleted file mode 100644 index f1e3b8b91508..000000000000 --- a/include/asm-v850/machdep.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * include/asm-v850/machdep.h -- Machine-dependent definitions - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_MACHDEP_H__ -#define __V850_MACHDEP_H__ - - -/* chips */ -#ifdef CONFIG_V850E_MA1 -#include -#endif -#ifdef CONFIG_V850E_ME2 -#include -#endif -#ifdef CONFIG_V850E_TEG -#include -#endif - -/* These are both chips _and_ platforms, so put them in the middle... */ -#ifdef CONFIG_V850E2_ANNA -#include -#endif -#ifdef CONFIG_V850E_AS85EP1 -#include -#endif - -/* platforms */ -#ifdef CONFIG_RTE_CB_MA1 -#include -#endif -#ifdef CONFIG_RTE_CB_ME2 -#include -#endif -#ifdef CONFIG_RTE_CB_NB85E -#include -#endif -#ifdef CONFIG_V850E_SIM -#include -#endif -#ifdef CONFIG_V850E2_SIM85E2C -#include -#endif -#ifdef CONFIG_V850E2_SIM85E2S -#include -#endif -#ifdef CONFIG_V850E2_FPGA85E2C -#include -#endif - -#endif /* __V850_MACHDEP_H__ */ diff --git a/include/asm-v850/macrology.h b/include/asm-v850/macrology.h deleted file mode 100644 index 37abf874832c..000000000000 --- a/include/asm-v850/macrology.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * include/asm-v850/macrology.h -- Various useful CPP macros - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#define macrology_paste(arg1, arg2) macrology_paste_1(arg1, arg2) -#define macrology_paste_1(arg1, arg2) arg1 ## arg2 -#define macrology_stringify(sym) macrology_stringify_1(sym) -#define macrology_stringify_1(sym) #sym diff --git a/include/asm-v850/me2.h b/include/asm-v850/me2.h deleted file mode 100644 index ac7c9ce0bdc1..000000000000 --- a/include/asm-v850/me2.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * include/asm-v850/me2.h -- V850E/ME2 cpu chip - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_ME2_H__ -#define __V850_ME2_H__ - -#include -#include - - -#define CPU_MODEL "v850e/me2" -#define CPU_MODEL_LONG "NEC V850E/ME2" - - -/* Hardware-specific interrupt numbers (in the kernel IRQ namespace). */ -#define IRQ_INTP(n) (n) /* Pnnn (pin) interrupts */ -#define IRQ_INTP_NUM 31 -#define IRQ_INTCMD(n) (0x31 + (n)) /* interval timer interrupts 0-3 */ -#define IRQ_INTCMD_NUM 4 -#define IRQ_INTDMA(n) (0x41 + (n)) /* DMA interrupts 0-3 */ -#define IRQ_INTDMA_NUM 4 -#define IRQ_INTUBTIRE(n) (0x49 + (n)*5)/* UARTB 0-1 reception error */ -#define IRQ_INTUBTIRE_NUM 2 -#define IRQ_INTUBTIR(n) (0x4a + (n)*5) /* UARTB 0-1 reception complete */ -#define IRQ_INTUBTIR_NUM 2 -#define IRQ_INTUBTIT(n) (0x4b + (n)*5) /* UARTB 0-1 transmission complete */ -#define IRQ_INTUBTIT_NUM 2 -#define IRQ_INTUBTIF(n) (0x4c + (n)*5) /* UARTB 0-1 FIFO trans. complete */ -#define IRQ_INTUBTIF_NUM 2 -#define IRQ_INTUBTITO(n) (0x4d + (n)*5) /* UARTB 0-1 reception timeout */ -#define IRQ_INTUBTITO_NUM 2 - -/* For */ -#define NUM_CPU_IRQS 0x59 /* V850E/ME2 */ - - -/* For */ -/* We use on-chip RAM, for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. */ -#define R0_RAM_ADDR 0xFFFFB000 /* V850E/ME2 */ - - -/* V850E/ME2 UARTB details.*/ -#define V850E_UART_NUM_CHANNELS 2 -#define V850E_UARTB_BASE_FREQ (CPU_CLOCK_FREQ / 4) - -/* This is a function that gets called before configuring the UART. */ -#define V850E_UART_PRE_CONFIGURE me2_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void me2_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif /* __ASSEMBLY__ */ - - -/* V850E/ME2 timer C details. */ -#define V850E_TIMER_C_BASE_ADDR 0xFFFFF600 - - -/* V850E/ME2 timer D details. */ -#define V850E_TIMER_D_BASE_ADDR 0xFFFFF540 -#define V850E_TIMER_D_TMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x0) -#define V850E_TIMER_D_CMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x2) -#define V850E_TIMER_D_TMCD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x4) - -#define V850E_TIMER_D_BASE_FREQ (CPU_CLOCK_FREQ / 2) - - -/* Select iRAM mode. */ -#define ME2_IRAMM_ADDR 0xFFFFF80A -#define ME2_IRAMM (*(volatile u8*)ME2_IRAMM_ADDR) - - -/* Interrupt edge-detection configuration. INTF(n) and INTR(n) are only - valid for n == 1, 2, or 5. */ -#define ME2_INTF_ADDR(n) (0xFFFFFC00 + (n) * 0x2) -#define ME2_INTF(n) (*(volatile u8*)ME2_INTF_ADDR(n)) -#define ME2_INTR_ADDR(n) (0xFFFFFC20 + (n) * 0x2) -#define ME2_INTR(n) (*(volatile u8*)ME2_INTR_ADDR(n)) -#define ME2_INTFAL_ADDR 0xFFFFFC10 -#define ME2_INTFAL (*(volatile u8*)ME2_INTFAL_ADDR) -#define ME2_INTRAL_ADDR 0xFFFFFC30 -#define ME2_INTRAL (*(volatile u8*)ME2_INTRAL_ADDR) -#define ME2_INTFDH_ADDR 0xFFFFFC16 -#define ME2_INTFDH (*(volatile u16*)ME2_INTFDH_ADDR) -#define ME2_INTRDH_ADDR 0xFFFFFC36 -#define ME2_INTRDH (*(volatile u16*)ME2_INTRDH_ADDR) -#define ME2_SESC_ADDR(n) (0xFFFFF609 + (n) * 0x10) -#define ME2_SESC(n) (*(volatile u8*)ME2_SESC_ADDR(n)) -#define ME2_SESA10_ADDR 0xFFFFF5AD -#define ME2_SESA10 (*(volatile u8*)ME2_SESA10_ADDR) -#define ME2_SESA11_ADDR 0xFFFFF5DD -#define ME2_SESA11 (*(volatile u8*)ME2_SESA11_ADDR) - - -/* Port 1 */ -/* Direct I/O. Bits 0-3 are pins P10-P13. */ -#define ME2_PORT1_IO_ADDR 0xFFFFF402 -#define ME2_PORT1_IO (*(volatile u8 *)ME2_PORT1_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ME2_PORT1_PM_ADDR 0xFFFFF422 -#define ME2_PORT1_PM (*(volatile u8 *)ME2_PORT1_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define ME2_PORT1_PMC_ADDR 0xFFFFF442 -#define ME2_PORT1_PMC (*(volatile u8 *)ME2_PORT1_PMC_ADDR) -/* Port function control (for serial interfaces, 0 = CSI30, 1 = UARTB0 ). */ -#define ME2_PORT1_PFC_ADDR 0xFFFFF462 -#define ME2_PORT1_PFC (*(volatile u8 *)ME2_PORT1_PFC_ADDR) - -/* Port 2 */ -/* Direct I/O. Bits 0-3 are pins P20-P25. */ -#define ME2_PORT2_IO_ADDR 0xFFFFF404 -#define ME2_PORT2_IO (*(volatile u8 *)ME2_PORT2_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ME2_PORT2_PM_ADDR 0xFFFFF424 -#define ME2_PORT2_PM (*(volatile u8 *)ME2_PORT2_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define ME2_PORT2_PMC_ADDR 0xFFFFF444 -#define ME2_PORT2_PMC (*(volatile u8 *)ME2_PORT2_PMC_ADDR) -/* Port function control (for serial interfaces, 0 = INTP2x, 1 = UARTB1 ). */ -#define ME2_PORT2_PFC_ADDR 0xFFFFF464 -#define ME2_PORT2_PFC (*(volatile u8 *)ME2_PORT2_PFC_ADDR) - -/* Port 5 */ -/* Direct I/O. Bits 0-5 are pins P50-P55. */ -#define ME2_PORT5_IO_ADDR 0xFFFFF40A -#define ME2_PORT5_IO (*(volatile u8 *)ME2_PORT5_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ME2_PORT5_PM_ADDR 0xFFFFF42A -#define ME2_PORT5_PM (*(volatile u8 *)ME2_PORT5_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define ME2_PORT5_PMC_ADDR 0xFFFFF44A -#define ME2_PORT5_PMC (*(volatile u8 *)ME2_PORT5_PMC_ADDR) -/* Port function control (). */ -#define ME2_PORT5_PFC_ADDR 0xFFFFF46A -#define ME2_PORT5_PFC (*(volatile u8 *)ME2_PORT5_PFC_ADDR) - -/* Port 6 */ -/* Direct I/O. Bits 5-7 are pins P65-P67. */ -#define ME2_PORT6_IO_ADDR 0xFFFFF40C -#define ME2_PORT6_IO (*(volatile u8 *)ME2_PORT6_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ME2_PORT6_PM_ADDR 0xFFFFF42C -#define ME2_PORT6_PM (*(volatile u8 *)ME2_PORT6_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define ME2_PORT6_PMC_ADDR 0xFFFFF44C -#define ME2_PORT6_PMC (*(volatile u8 *)ME2_PORT6_PMC_ADDR) -/* Port function control (). */ -#define ME2_PORT6_PFC_ADDR 0xFFFFF46C -#define ME2_PORT6_PFC (*(volatile u8 *)ME2_PORT6_PFC_ADDR) - -/* Port 7 */ -/* Direct I/O. Bits 2-7 are pins P72-P77. */ -#define ME2_PORT7_IO_ADDR 0xFFFFF40E -#define ME2_PORT7_IO (*(volatile u8 *)ME2_PORT7_IO_ADDR) -/* Port mode (for direct I/O, 0 = output, 1 = input). */ -#define ME2_PORT7_PM_ADDR 0xFFFFF42E -#define ME2_PORT7_PM (*(volatile u8 *)ME2_PORT7_PM_ADDR) -/* Port mode control (0 = direct I/O mode, 1 = alternative I/O mode). */ -#define ME2_PORT7_PMC_ADDR 0xFFFFF44E -#define ME2_PORT7_PMC (*(volatile u8 *)ME2_PORT7_PMC_ADDR) -/* Port function control (). */ -#define ME2_PORT7_PFC_ADDR 0xFFFFF46E -#define ME2_PORT7_PFC (*(volatile u8 *)ME2_PORT7_PFC_ADDR) - - -#ifndef __ASSEMBLY__ -/* Initialize V850E/ME2 chip interrupts. */ -extern void me2_init_irqs (void); -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_ME2_H__ */ diff --git a/include/asm-v850/mman.h b/include/asm-v850/mman.h deleted file mode 100644 index edbf6edbfb37..000000000000 --- a/include/asm-v850/mman.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __V850_MMAN_H__ -#define __V850_MMAN_H__ - -#include - -#define MAP_GROWSDOWN 0x0100 /* stack-like segment */ -#define MAP_DENYWRITE 0x0800 /* ETXTBSY */ -#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ -#define MAP_LOCKED 0x2000 /* pages are locked */ -#define MAP_NORESERVE 0x4000 /* don't check for reservations */ - -#define MCL_CURRENT 1 /* lock all current mappings */ -#define MCL_FUTURE 2 /* lock all future mappings */ - -#endif /* __V850_MMAN_H__ */ diff --git a/include/asm-v850/mmu.h b/include/asm-v850/mmu.h deleted file mode 100644 index 267768c66ef6..000000000000 --- a/include/asm-v850/mmu.h +++ /dev/null @@ -1,11 +0,0 @@ -/* Copyright (C) 2002, 2005, David McCullough */ - -#ifndef __V850_MMU_H__ -#define __V850_MMU_H__ - -typedef struct { - struct vm_list_struct *vmlist; - unsigned long end_brk; -} mm_context_t; - -#endif /* __V850_MMU_H__ */ diff --git a/include/asm-v850/mmu_context.h b/include/asm-v850/mmu_context.h deleted file mode 100644 index 01daacd5474e..000000000000 --- a/include/asm-v850/mmu_context.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __V850_MMU_CONTEXT_H__ -#define __V850_MMU_CONTEXT_H__ - -#include - -#define destroy_context(mm) ((void)0) -#define init_new_context(tsk,mm) 0 -#define switch_mm(prev,next,tsk) ((void)0) -#define deactivate_mm(tsk,mm) do { } while (0) -#define activate_mm(prev,next) ((void)0) -#define enter_lazy_tlb(mm,tsk) ((void)0) - -#endif /* __V850_MMU_CONTEXT_H__ */ diff --git a/include/asm-v850/module.h b/include/asm-v850/module.h deleted file mode 100644 index 2c2f4944f09f..000000000000 --- a/include/asm-v850/module.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * include/asm-v850/module.h -- Architecture-specific module hooks - * - * Copyright (C) 2001,02,03,04 NEC Corporation - * Copyright (C) 2001,02,03,04 Miles Bader - * Copyright (C) 2001,03 Rusty Russell - * - * 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 for more details. - * - * Written by Miles Bader - * - * Derived in part from include/asm-ppc/module.h - */ - -#ifndef __V850_MODULE_H__ -#define __V850_MODULE_H__ - -#define MODULE_SYMBOL_PREFIX "_" - -struct v850_plt_entry -{ - /* Indirect jump instruction sequence (6-byte mov + 2-byte jr). */ - unsigned long tramp[2]; -}; - -struct mod_arch_specific -{ - /* Indices of PLT sections within module. */ - unsigned int core_plt_section, init_plt_section; -}; - -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr - -/* Make empty sections for module_frob_arch_sections to expand. */ -#ifdef MODULE -asm(".section .plt,\"ax\",@nobits; .align 3; .previous"); -asm(".section .init.plt,\"ax\",@nobits; .align 3; .previous"); -#endif - -/* We don't do exception tables. */ -struct exception_table_entry; -static inline const struct exception_table_entry * -search_extable(const struct exception_table_entry *first, - const struct exception_table_entry *last, - unsigned long value) -{ - return 0; -} -#define ARCH_HAS_SEARCH_EXTABLE -static inline void -sort_extable(struct exception_table_entry *start, - struct exception_table_entry *finish) -{ - /* nada */ -} -#define ARCH_HAS_SORT_EXTABLE - -#endif /* __V850_MODULE_H__ */ diff --git a/include/asm-v850/msgbuf.h b/include/asm-v850/msgbuf.h deleted file mode 100644 index ed07dbd01637..000000000000 --- a/include/asm-v850/msgbuf.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef __V850_MSGBUF_H__ -#define __V850_MSGBUF_H__ - -/* - * The msqid64_ds structure for v850 architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 64-bit time_t to solve y2038 problem - * - 2 miscellaneous 32-bit values - */ - -struct msqid64_ds { - struct ipc64_perm msg_perm; - __kernel_time_t msg_stime; /* last msgsnd time */ - unsigned long __unused1; - __kernel_time_t msg_rtime; /* last msgrcv time */ - unsigned long __unused2; - __kernel_time_t msg_ctime; /* last change time */ - unsigned long __unused3; - unsigned long msg_cbytes; /* current number of bytes on queue */ - unsigned long msg_qnum; /* number of messages in queue */ - unsigned long msg_qbytes; /* max number of bytes on queue */ - __kernel_pid_t msg_lspid; /* pid of last msgsnd */ - __kernel_pid_t msg_lrpid; /* last receive pid */ - unsigned long __unused4; - unsigned long __unused5; -}; - -#endif /* __V850_MSGBUF_H__ */ diff --git a/include/asm-v850/mutex.h b/include/asm-v850/mutex.h deleted file mode 100644 index 458c1f7fbc18..000000000000 --- a/include/asm-v850/mutex.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Pull in the generic implementation for the mutex fastpath. - * - * TODO: implement optimized primitives instead, or leave the generic - * implementation in place, or pick the atomic_xchg() based generic - * implementation. (see asm-generic/mutex-xchg.h for details) - */ - -#include diff --git a/include/asm-v850/page.h b/include/asm-v850/page.h deleted file mode 100644 index f9de35d873fa..000000000000 --- a/include/asm-v850/page.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * include/asm-v850/page.h -- VM ops - * - * Copyright (C) 2001,02,03,05 NEC Electronics Corporation - * Copyright (C) 2001,02,03,05 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PAGE_H__ -#define __V850_PAGE_H__ - -#include - - -#define PAGE_SHIFT 12 -#define PAGE_SIZE (1UL << PAGE_SHIFT) -#define PAGE_MASK (~(PAGE_SIZE-1)) - - -/* - * PAGE_OFFSET -- the first address of the first page of memory. For archs with - * no MMU this corresponds to the first free page in physical memory (aligned - * on a page boundary). - */ -#ifndef PAGE_OFFSET -#define PAGE_OFFSET 0x0000000 -#endif - - -#ifndef __ASSEMBLY__ - -#define STRICT_MM_TYPECHECKS - -#define clear_page(page) memset ((void *)(page), 0, PAGE_SIZE) -#define copy_page(to, from) memcpy ((void *)(to), (void *)from, PAGE_SIZE) - -#define clear_user_page(addr, vaddr, page) \ - do { clear_page(addr); \ - flush_dcache_page(page); \ - } while (0) -#define copy_user_page(to, from, vaddr, page) \ - do { copy_page(to, from); \ - flush_dcache_page(page); \ - } while (0) - -#ifdef STRICT_MM_TYPECHECKS -/* - * These are used to make use of C type-checking.. - */ - -typedef struct { unsigned long pte; } pte_t; -typedef struct { unsigned long pmd; } pmd_t; -typedef struct { unsigned long pgd; } pgd_t; -typedef struct { unsigned long pgprot; } pgprot_t; -typedef struct page *pgtable_t; - -#define pte_val(x) ((x).pte) -#define pmd_val(x) ((x).pmd) -#define pgd_val(x) ((x).pgd) -#define pgprot_val(x) ((x).pgprot) - -#define __pte(x) ((pte_t) { (x) } ) -#define __pmd(x) ((pmd_t) { (x) } ) -#define __pgd(x) ((pgd_t) { (x) } ) -#define __pgprot(x) ((pgprot_t) { (x) } ) - -#else /* !STRICT_MM_TYPECHECKS */ -/* - * .. while these make it easier on the compiler - */ - -typedef unsigned long pte_t; -typedef unsigned long pmd_t; -typedef unsigned long pgd_t; -typedef unsigned long pgprot_t; - -#define pte_val(x) (x) -#define pmd_val(x) (x) -#define pgd_val(x) (x) -#define pgprot_val(x) (x) - -#define __pte(x) (x) -#define __pmd(x) (x) -#define __pgd(x) (x) -#define __pgprot(x) (x) - -#endif /* STRICT_MM_TYPECHECKS */ - -#endif /* !__ASSEMBLY__ */ - - -/* No current v850 processor has virtual memory. */ -#define __virt_to_phys(addr) (addr) -#define __phys_to_virt(addr) (addr) - -#define virt_to_pfn(kaddr) (__virt_to_phys (kaddr) >> PAGE_SHIFT) -#define pfn_to_virt(pfn) __phys_to_virt ((pfn) << PAGE_SHIFT) - -#define MAP_NR(kaddr) \ - (((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT) -#define virt_to_page(kaddr) (mem_map + MAP_NR (kaddr)) -#define page_to_virt(page) \ - ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) - -#define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) -#define pfn_valid(pfn) ((pfn) < max_mapnr) - -#define virt_addr_valid(kaddr) \ - (((void *)(kaddr) >= (void *)PAGE_OFFSET) && MAP_NR (kaddr) < max_mapnr) - - -#define __pa(x) __virt_to_phys ((unsigned long)(x)) -#define __va(x) ((void *)__phys_to_virt ((unsigned long)(x))) - - -#include -#include - -#endif /* __V850_PAGE_H__ */ diff --git a/include/asm-v850/param.h b/include/asm-v850/param.h deleted file mode 100644 index 4391f5fe0204..000000000000 --- a/include/asm-v850/param.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * include/asm-v850/param.h -- Varions kernel parameters - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PARAM_H__ -#define __V850_PARAM_H__ - -#define EXEC_PAGESIZE 4096 - -#ifndef NOGROUP -#define NOGROUP (-1) -#endif - -#define MAXHOSTNAMELEN 64 /* max length of hostname */ - -#ifdef __KERNEL__ -# define HZ CONFIG_HZ -# define USER_HZ 100 -# define CLOCKS_PER_SEC USER_HZ -#else -# define HZ 100 -#endif - -#endif /* __V850_PARAM_H__ */ diff --git a/include/asm-v850/pci.h b/include/asm-v850/pci.h deleted file mode 100644 index de2a7d0a81cc..000000000000 --- a/include/asm-v850/pci.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * include/asm-v850/pci.h -- PCI support - * - * Copyright (C) 2001,02,05 NEC Corporation - * Copyright (C) 2001,02,05 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PCI_H__ -#define __V850_PCI_H__ - -/* Get any platform-dependent definitions. */ -#include - -#define pcibios_scan_all_fns(a, b) 0 - -/* Generic declarations. */ - -struct scatterlist; - -extern void pcibios_set_master (struct pci_dev *dev); - -/* `Grant' to PDEV the memory block at CPU_ADDR, for doing DMA. The - 32-bit PCI bus mastering address to use is returned. the device owns - this memory until either pci_unmap_single or pci_dma_sync_single_for_cpu is - performed. */ -extern dma_addr_t -pci_map_single (struct pci_dev *pdev, void *cpu_addr, size_t size, int dir); - -/* Return to the CPU the PCI DMA memory block previously `granted' to - PDEV, at DMA_ADDR. */ -extern void -pci_unmap_single (struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, - int dir); - -/* Make physical memory consistent for a single streaming mode DMA - translation after a transfer. - - If you perform a pci_map_single() but wish to interrogate the - buffer using the cpu, yet do not wish to teardown the PCI dma - mapping, you must call this function before doing so. At the next - point you give the PCI dma address back to the card, you must first - perform a pci_dma_sync_for_device, and then the device again owns - the buffer. */ -extern void -pci_dma_sync_single_for_cpu (struct pci_dev *dev, dma_addr_t dma_addr, - size_t size, int dir); - -extern void -pci_dma_sync_single_for_device (struct pci_dev *dev, dma_addr_t dma_addr, - size_t size, int dir); - - -/* Do multiple DMA mappings at once. */ -extern int -pci_map_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len, int dir); - -/* Unmap multiple DMA mappings at once. */ -extern void -pci_unmap_sg (struct pci_dev *pdev, struct scatterlist *sg, int sg_len, - int dir); - -/* SG-list versions of pci_dma_sync functions. */ -extern void -pci_dma_sync_sg_for_cpu (struct pci_dev *dev, - struct scatterlist *sg, int sg_len, - int dir); -extern void -pci_dma_sync_sg_for_device (struct pci_dev *dev, - struct scatterlist *sg, int sg_len, - int dir); - -#define pci_map_page(dev, page, offs, size, dir) \ - pci_map_single(dev, (page_address(page) + (offs)), size, dir) -#define pci_unmap_page(dev,addr,sz,dir) \ - pci_unmap_single(dev, addr, sz, dir) - -/* Test for pci_map_single or pci_map_page having generated an error. */ -static inline int -pci_dma_mapping_error (dma_addr_t dma_addr) -{ - return dma_addr == 0; -} - -/* Allocate and map kernel buffer using consistent mode DMA for PCI - device. Returns non-NULL cpu-view pointer to the buffer if - successful and sets *DMA_ADDR to the pci side dma address as well, - else DMA_ADDR is undefined. */ -extern void * -pci_alloc_consistent (struct pci_dev *pdev, size_t size, dma_addr_t *dma_addr); - -/* Free and unmap a consistent DMA buffer. CPU_ADDR and DMA_ADDR must - be values that were returned from pci_alloc_consistent. SIZE must be - the same as what as passed into pci_alloc_consistent. References to - the memory and mappings assosciated with CPU_ADDR or DMA_ADDR past - this call are illegal. */ -extern void -pci_free_consistent (struct pci_dev *pdev, size_t size, void *cpu_addr, - dma_addr_t dma_addr); - -#ifdef CONFIG_PCI -static inline void pci_dma_burst_advice(struct pci_dev *pdev, - enum pci_dma_burst_strategy *strat, - unsigned long *strategy_parameter) -{ - *strat = PCI_DMA_BURST_INFINITY; - *strategy_parameter = ~0UL; -} -#endif - -extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); -extern void pci_iounmap (struct pci_dev *dev, void __iomem *addr); - -#endif /* __V850_PCI_H__ */ diff --git a/include/asm-v850/percpu.h b/include/asm-v850/percpu.h deleted file mode 100644 index 755ac6522b63..000000000000 --- a/include/asm-v850/percpu.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __V850_PERCPU_H__ -#define __V850_PERCPU_H__ - -#include - -/* This is a stupid hack to satisfy some grotty implicit include-file - dependency; basically, uses BUG_ON, which calls BUG, but - doesn't include the necessary headers to define it. In the twisted - festering mess of includes this must all be resolved somehow on other - platforms, but I haven't the faintest idea how, and don't care; here will - do, even though doesn't actually make any sense. */ -#include - -#endif /* __V850_PERCPU_H__ */ diff --git a/include/asm-v850/pgalloc.h b/include/asm-v850/pgalloc.h deleted file mode 100644 index b91eb2d02bfd..000000000000 --- a/include/asm-v850/pgalloc.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * include/asm-v850/pgalloc.h - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PGALLOC_H__ -#define __V850_PGALLOC_H__ - -#include /* some crap code expects this */ - -/* ... and then, there was one. */ -#define check_pgt_cache() ((void)0) - -#endif /* __V850_PGALLOC_H__ */ diff --git a/include/asm-v850/pgtable.h b/include/asm-v850/pgtable.h deleted file mode 100644 index 1ea2a900f0f8..000000000000 --- a/include/asm-v850/pgtable.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef __V850_PGTABLE_H__ -#define __V850_PGTABLE_H__ - -#include - -#include - - -#define pgd_present(pgd) (1) /* pages are always present on NO_MM */ -#define pgd_none(pgd) (0) -#define pgd_bad(pgd) (0) -#define pgd_clear(pgdp) ((void)0) - -#define pmd_offset(a, b) ((void *)0) - -#define kern_addr_valid(addr) (1) - - -#define __swp_type(x) (0) -#define __swp_offset(x) (0) -#define __swp_entry(typ,off) ((swp_entry_t) { ((typ) | ((off) << 7)) }) -#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) -#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) - -static inline int pte_file (pte_t pte) { return 0; } - - -/* These mean nothing to !CONFIG_MMU. */ -#define PAGE_NONE __pgprot(0) -#define PAGE_SHARED __pgprot(0) -#define PAGE_COPY __pgprot(0) -#define PAGE_READONLY __pgprot(0) -#define PAGE_KERNEL __pgprot(0) - - -/* - * ZERO_PAGE is a global shared page that is always zero: used - * for zero-mapped memory areas etc. When CONFIG_MMU is not defined, this - * should never actually be used, so just define it to something that's - * will hopefully cause a bus error if it is. - */ -#define ZERO_PAGE(vaddr) ((void *)0x87654321) - - -/* Some bogus code in procfs uses these; whatever. */ -#define VMALLOC_START 0 -#define VMALLOC_END (~0) - - -extern void paging_init (void); -#define swapper_pg_dir ((pgd_t *) 0) - -#define pgtable_cache_init() ((void)0) - - -extern unsigned int kobjsize(const void *objp); - - -#endif /* __V850_PGTABLE_H__ */ diff --git a/include/asm-v850/poll.h b/include/asm-v850/poll.h deleted file mode 100644 index 803cad0b9b59..000000000000 --- a/include/asm-v850/poll.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __V850_POLL_H__ -#define __V850_POLL_H__ - -#define POLLWRNORM POLLOUT -#define POLLWRBAND 0x0100 - -#include - -#endif /* __V850_POLL_H__ */ diff --git a/include/asm-v850/posix_types.h b/include/asm-v850/posix_types.h deleted file mode 100644 index 7f403b765390..000000000000 --- a/include/asm-v850/posix_types.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * include/asm-v850/posix_types.h -- Kernel versions of standard types - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_POSIX_TYPES_H__ -#define __V850_POSIX_TYPES_H__ - -typedef unsigned long __kernel_ino_t; -typedef unsigned long long __kernel_ino64_t; -typedef unsigned int __kernel_mode_t; -typedef unsigned int __kernel_nlink_t; -typedef long __kernel_off_t; -typedef long long __kernel_loff_t; -typedef int __kernel_pid_t; -typedef unsigned short __kernel_ipc_pid_t; -typedef unsigned int __kernel_uid_t; -typedef unsigned int __kernel_gid_t; -typedef unsigned int __kernel_size_t; -typedef int __kernel_ssize_t; -typedef int __kernel_ptrdiff_t; -typedef long __kernel_time_t; -typedef long __kernel_suseconds_t; -typedef long __kernel_clock_t; -typedef int __kernel_timer_t; -typedef int __kernel_clockid_t; -typedef int __kernel_daddr_t; -typedef char * __kernel_caddr_t; -typedef unsigned short __kernel_uid16_t; -typedef unsigned short __kernel_gid16_t; -typedef unsigned int __kernel_uid32_t; -typedef unsigned int __kernel_gid32_t; - -/* Some bogus code depends on this; we don't care. */ -typedef __kernel_uid_t __kernel_old_uid_t; -typedef unsigned int __kernel_old_dev_t; - -typedef struct { - int val[2]; -} __kernel_fsid_t; - - -#if defined(__KERNEL__) - -/* We used to include here, which seems the right thing, but - it caused nasty include-file definition order problems. Removing the - include seems to work, so fingers crossed... */ - -#undef __FD_SET -#define __FD_SET(fd, fd_set) \ - __set_bit (fd, (void *)&((__kernel_fd_set *)fd_set)->fds_bits) -#undef __FD_CLR -#define __FD_CLR(fd, fd_set) \ - __clear_bit (fd, (void *)&((__kernel_fd_set *)fd_set)->fds_bits) -#undef __FD_ISSET -#define __FD_ISSET(fd, fd_set) \ - __test_bit (fd, (void *)&((__kernel_fd_set *)fd_set)->fds_bits) -#undef __FD_ZERO -#define __FD_ZERO(fd_set) \ - memset (fd_set, 0, sizeof (*(fd_set *)fd_set)) - -#endif /* defined(__KERNEL__) */ - -#endif /* __V850_POSIX_TYPES_H__ */ diff --git a/include/asm-v850/processor.h b/include/asm-v850/processor.h deleted file mode 100644 index 979e3467f9af..000000000000 --- a/include/asm-v850/processor.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * include/asm-v850/processor.h - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PROCESSOR_H__ -#define __V850_PROCESSOR_H__ - -#ifndef __ASSEMBLY__ /* is not asm-safe. */ -#include -#endif - -#include -#include -#include - -/* Some code expects `segment' stuff to be defined here. */ -#include - - -/* - * The only places this is used seem to be horrible bletcherous kludges, - * so we just define it to be as large as possible. - */ -#define TASK_SIZE (0xFFFFFFFF) - -/* - * This decides where the kernel will search for a free chunk of vm - * space during mmap's. We won't be using it. - */ -#define TASK_UNMAPPED_BASE 0 - - -#ifndef __ASSEMBLY__ - - -/* - * Default implementation of macro that returns current - * instruction pointer ("program counter"). - */ -#define current_text_addr() ({ __label__ _l; _l: &&_l;}) - -/* If you change this, you must change the associated assembly-languages - constants defined below, THREAD_*. */ -struct thread_struct { - /* kernel stack pointer (must be first field in structure) */ - unsigned long ksp; -}; - -#define INIT_THREAD { sizeof init_stack + (unsigned long)init_stack } - - -/* Do necessary setup to start up a newly executed thread. */ -static inline void start_thread (struct pt_regs *regs, - unsigned long pc, unsigned long usp) -{ - regs->pc = pc; - regs->gpr[GPR_SP] = usp; - regs->kernel_mode = 0; -} - -/* Free all resources held by a thread. */ -static inline void release_thread (struct task_struct *dead_task) -{ -} - -/* Prepare to copy thread state - unlazy all lazy status */ -#define prepare_to_copy(tsk) do { } while (0) - -extern int kernel_thread (int (*fn)(void *), void * arg, unsigned long flags); - -/* Free current thread data structures etc. */ -static inline void exit_thread (void) -{ -} - - -/* Return the registers saved during context-switch by the currently - not-running thread T. Note that this only includes some registers! - See entry.S for details. */ -#define thread_saved_regs(t) \ - ((struct pt_regs*)((t)->thread.ksp + STATE_SAVE_PT_OFFSET)) -/* Return saved (kernel) PC of a blocked thread. Actually, we return the - LP register, because the thread is actually blocked in switch_thread, - and we're interested in the PC it will _return_ to. */ -#define thread_saved_pc(t) (thread_saved_regs(t)->gpr[GPR_LP]) - - -unsigned long get_wchan (struct task_struct *p); - - -/* Return some info about the user process TASK. */ -#define task_tos(task) ((unsigned long)task_stack_page(task) + THREAD_SIZE) -#define task_pt_regs(task) ((struct pt_regs *)task_tos (task) - 1) -#define task_sp(task) (task_pt_regs (task)->gpr[GPR_SP]) -#define task_pc(task) (task_pt_regs (task)->pc) -/* Grotty old names for some. */ -#define KSTK_EIP(task) task_pc (task) -#define KSTK_ESP(task) task_sp (task) - - -#define cpu_relax() barrier() - - -#else /* __ASSEMBLY__ */ - -#define THREAD_KSP 0 - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_PROCESSOR_H__ */ diff --git a/include/asm-v850/ptrace.h b/include/asm-v850/ptrace.h deleted file mode 100644 index 4f35cf2cd641..000000000000 --- a/include/asm-v850/ptrace.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * include/asm-v850/ptrace.h -- Access to CPU registers - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_PTRACE_H__ -#define __V850_PTRACE_H__ - - -/* v850 general purpose registers with special meanings. */ -#define GPR_ZERO 0 /* constant zero */ -#define GPR_ASM 1 /* reserved for assembler */ -#define GPR_SP 3 /* stack pointer */ -#define GPR_GP 4 /* global data pointer */ -#define GPR_TP 5 /* `text pointer' */ -#define GPR_EP 30 /* `element pointer' */ -#define GPR_LP 31 /* link pointer (current return address) */ - -/* These aren't official names, but they make some code more descriptive. */ -#define GPR_ARG0 6 -#define GPR_ARG1 7 -#define GPR_ARG2 8 -#define GPR_ARG3 9 -#define GPR_RVAL0 10 -#define GPR_RVAL1 11 -#define GPR_RVAL GPR_RVAL0 - -#define NUM_GPRS 32 - -/* v850 `system' registers. */ -#define SR_EIPC 0 -#define SR_EIPSW 1 -#define SR_FEPC 2 -#define SR_FEPSW 3 -#define SR_ECR 4 -#define SR_PSW 5 -#define SR_CTPC 16 -#define SR_CTPSW 17 -#define SR_DBPC 18 -#define SR_DBPSW 19 -#define SR_CTBP 20 -#define SR_DIR 21 -#define SR_ASID 23 - - -#ifndef __ASSEMBLY__ - -typedef unsigned long v850_reg_t; - -/* How processor state is stored on the stack during a syscall/signal. - If you change this structure, change the associated assembly-language - macros below too (PT_*)! */ -struct pt_regs -{ - /* General purpose registers. */ - v850_reg_t gpr[NUM_GPRS]; - - v850_reg_t pc; /* program counter */ - v850_reg_t psw; /* program status word */ - - /* Registers used by `callt' instruction: */ - v850_reg_t ctpc; /* saved program counter */ - v850_reg_t ctpsw; /* saved psw */ - v850_reg_t ctbp; /* base pointer for callt table */ - - char kernel_mode; /* 1 if in `kernel mode', 0 if user mode */ -}; - - -#define instruction_pointer(regs) ((regs)->pc) -#define profile_pc(regs) instruction_pointer(regs) -#define user_mode(regs) (!(regs)->kernel_mode) - -/* When a struct pt_regs is used to save user state for a system call in - the kernel, the system call is stored in the space for R0 (since it's - never used otherwise, R0 being a constant 0). Non-system-calls - simply store 0 there. */ -#define PT_REGS_SYSCALL(regs) (regs)->gpr[0] -#define PT_REGS_SET_SYSCALL(regs, val) ((regs)->gpr[0] = (val)) - -#endif /* !__ASSEMBLY__ */ - - -/* The number of bytes used to store each register. */ -#define _PT_REG_SIZE 4 - -/* Offset of a general purpose register in a struct pt_regs. */ -#define PT_GPR(num) ((num) * _PT_REG_SIZE) - -/* Offsets of various special registers & fields in a struct pt_regs. */ -#define PT_PC ((NUM_GPRS + 0) * _PT_REG_SIZE) -#define PT_PSW ((NUM_GPRS + 1) * _PT_REG_SIZE) -#define PT_CTPC ((NUM_GPRS + 2) * _PT_REG_SIZE) -#define PT_CTPSW ((NUM_GPRS + 3) * _PT_REG_SIZE) -#define PT_CTBP ((NUM_GPRS + 4) * _PT_REG_SIZE) -#define PT_KERNEL_MODE ((NUM_GPRS + 5) * _PT_REG_SIZE) - -/* Where the current syscall number is stashed; obviously only valid in - the kernel! */ -#define PT_CUR_SYSCALL PT_GPR(0) - -/* Size of struct pt_regs, including alignment. */ -#define PT_SIZE ((NUM_GPRS + 6) * _PT_REG_SIZE) - - -/* These are `magic' values for PTRACE_PEEKUSR that return info about where - a process is located in memory. */ -#define PT_TEXT_ADDR (PT_SIZE + 1) -#define PT_TEXT_LEN (PT_SIZE + 2) -#define PT_DATA_ADDR (PT_SIZE + 3) - - -#endif /* __V850_PTRACE_H__ */ diff --git a/include/asm-v850/resource.h b/include/asm-v850/resource.h deleted file mode 100644 index 4b9dcd44f8d1..000000000000 --- a/include/asm-v850/resource.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_RESOURCE_H__ -#define __V850_RESOURCE_H__ - -#include - -#endif /* __V850_RESOURCE_H__ */ diff --git a/include/asm-v850/rte_cb.h b/include/asm-v850/rte_cb.h deleted file mode 100644 index db9879f00aa7..000000000000 --- a/include/asm-v850/rte_cb.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * include/asm-v850/rte_cb.h -- Midas labs RTE-CB series of evaluation boards - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_RTE_CB_H__ -#define __V850_RTE_CB_H__ - - -/* The SRAM on the Mother-A motherboard. */ -#define MB_A_SRAM_ADDR GCS0_ADDR -#define MB_A_SRAM_SIZE 0x00200000 /* 2MB */ - - -#ifdef CONFIG_RTE_GBUS_INT -/* GBUS interrupt support. */ - -# include - -# define GBUS_INT_BASE_IRQ NUM_RTE_CB_IRQS -# define GBUS_INT_BASE_ADDR (GCS2_ADDR + 0x00006000) - -/* Some specific interrupts. */ -# define IRQ_MB_A_LAN IRQ_GBUS_INT(10) -# define IRQ_MB_A_PCI1(n) (IRQ_GBUS_INT(16) + (n)) -# define IRQ_MB_A_PCI1_NUM 4 -# define IRQ_MB_A_PCI2(n) (IRQ_GBUS_INT(20) + (n)) -# define IRQ_MB_A_PCI2_NUM 4 -# define IRQ_MB_A_EXT(n) (IRQ_GBUS_INT(24) + (n)) -# define IRQ_MB_A_EXT_NUM 4 -# define IRQ_MB_A_USB_OC(n) (IRQ_GBUS_INT(28) + (n)) -# define IRQ_MB_A_USB_OC_NUM 2 -# define IRQ_MB_A_PCMCIA_OC IRQ_GBUS_INT(30) - -/* We define NUM_MACH_IRQS to include extra interrupts from the GBUS. */ -# define NUM_MACH_IRQS (NUM_RTE_CB_IRQS + IRQ_GBUS_INT_NUM) - -#else /* !CONFIG_RTE_GBUS_INT */ - -# define NUM_MACH_IRQS NUM_RTE_CB_IRQS - -#endif /* CONFIG_RTE_GBUS_INT */ - - -#ifdef CONFIG_RTE_MB_A_PCI -/* Mother-A PCI bus support. */ - -# include - -/* These are the base addresses used for allocating device address - space. 512K of the motherboard SRAM is in the same space, so we have - to be careful not to let it be allocated. */ -# define PCIBIOS_MIN_MEM (MB_A_PCI_MEM_ADDR + 0x80000) -# define PCIBIOS_MIN_IO MB_A_PCI_IO_ADDR - -/* As we don't really support PCI DMA to cpu memory, and use bounce-buffers - instead, perversely enough, this becomes always true! */ -# define pci_dma_supported(dev, mask) 1 -# define pcibios_assign_all_busses() 1 - -#endif /* CONFIG_RTE_MB_A_PCI */ - - -#ifndef __ASSEMBLY__ -extern void rte_cb_early_init (void); -extern void rte_cb_init_irqs (void); -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_RTE_CB_H__ */ diff --git a/include/asm-v850/rte_ma1_cb.h b/include/asm-v850/rte_ma1_cb.h deleted file mode 100644 index bd3162ab9844..000000000000 --- a/include/asm-v850/rte_ma1_cb.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * include/asm-v850/rte_ma1_cb.h -- Midas labs RTE-V850/MA1-CB board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_RTE_MA1_CB_H__ -#define __V850_RTE_MA1_CB_H__ - -#include /* Common defs for Midas RTE-CB boards. */ - - -#define PLATFORM "rte-v850e/ma1-cb" -#define PLATFORM_LONG "Midas lab RTE-V850E/MA1-CB" - -#define CPU_CLOCK_FREQ 50000000 /* 50MHz */ - -/* 1MB of onboard SRAM. Note that the monitor ROM uses parts of this - for its own purposes, so care must be taken. Some address lines are - not decoded, so the SRAM area is mirrored every 1MB from 0x400000 to - 0x800000 (exclusive). */ -#define SRAM_ADDR 0x00400000 -#define SRAM_SIZE 0x00100000 /* 1MB */ - -/* 32MB of onbard SDRAM. */ -#define SDRAM_ADDR 0x00800000 -#define SDRAM_SIZE 0x02000000 /* 32MB */ - - -/* CPU addresses of GBUS memory spaces. */ -#define GCS0_ADDR 0x05000000 /* GCS0 - Common SRAM (2MB) */ -#define GCS0_SIZE 0x00200000 /* 2MB */ -#define GCS1_ADDR 0x06000000 /* GCS1 - Flash ROM (8MB) */ -#define GCS1_SIZE 0x00800000 /* 8MB */ -#define GCS2_ADDR 0x07900000 /* GCS2 - I/O registers */ -#define GCS2_SIZE 0x00400000 /* 4MB */ -#define GCS5_ADDR 0x04000000 /* GCS5 - PCI bus space */ -#define GCS5_SIZE 0x01000000 /* 16MB */ -#define GCS6_ADDR 0x07980000 /* GCS6 - PCI control registers */ -#define GCS6_SIZE 0x00000200 /* 512B */ - - -/* For */ -#define PAGE_OFFSET SRAM_ADDR - - -/* The GBUS GINT0 - GINT3 interrupts are connected to the INTP000 - INTP011 - pins on the CPU. These are shared among the GBUS interrupts. */ -#define IRQ_GINT(n) IRQ_INTP(n) -#define IRQ_GINT_NUM 4 - -/* Used by to derive NUM_MACH_IRQS. */ -#define NUM_RTE_CB_IRQS NUM_CPU_IRQS - - -#ifdef CONFIG_ROM_KERNEL -/* Kernel is in ROM, starting at address 0. */ - -#define INTV_BASE 0 - -#else /* !CONFIG_ROM_KERNEL */ - -#ifdef CONFIG_RTE_CB_MULTI -/* Using RAM kernel with ROM monitor for Multi debugger. */ - -/* The chip's real interrupt vectors are in ROM, but they jump to a - secondary interrupt vector table in RAM. */ -#define INTV_BASE 0x004F8000 - -/* Scratch memory used by the ROM monitor, which shouldn't be used by - linux (except for the alternate interrupt vector area, defined - above). */ -#define MON_SCRATCH_ADDR 0x004F8000 -#define MON_SCRATCH_SIZE 0x00008000 /* 32KB */ - -#else /* !CONFIG_RTE_CB_MULTI */ -/* Using RAM-kernel. Assume some sort of boot-loader got us loaded at - address 0. */ - -#define INTV_BASE 0 - -#endif /* CONFIG_RTE_CB_MULTI */ - -#endif /* CONFIG_ROM_KERNEL */ - - -/* Some misc. on-board devices. */ - -/* Seven-segment LED display (two digits). Write-only. */ -#define LED_ADDR(n) (0x07802000 + (n)) -#define LED(n) (*(volatile unsigned char *)LED_ADDR(n)) -#define LED_NUM_DIGITS 2 - - -/* Override the basic MA uart pre-initialization so that we can - initialize extra stuff. */ -#undef V850E_UART_PRE_CONFIGURE /* should be defined by */ -#define V850E_UART_PRE_CONFIGURE rte_ma1_cb_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void rte_ma1_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - -/* This board supports RTS/CTS for the on-chip UART, but only for channel 0. */ - -/* CTS for UART channel 0 is pin P43 (bit 3 of port 4). */ -#define V850E_UART_CTS(chan) ((chan) == 0 ? !(MA_PORT4_IO & 0x8) : 1) -/* RTS for UART channel 0 is pin P42 (bit 2 of port 4). */ -#define V850E_UART_SET_RTS(chan, val) \ - do { \ - if (chan == 0) { \ - unsigned old = MA_PORT4_IO; \ - if (val) \ - MA_PORT4_IO = old & ~0x4; \ - else \ - MA_PORT4_IO = old | 0x4; \ - } \ - } while (0) - - -#endif /* __V850_RTE_MA1_CB_H__ */ diff --git a/include/asm-v850/rte_mb_a_pci.h b/include/asm-v850/rte_mb_a_pci.h deleted file mode 100644 index 41ac185ca9cd..000000000000 --- a/include/asm-v850/rte_mb_a_pci.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * include/asm-v850/mb_a_pci.h -- PCI support for Midas lab RTE-MOTHER-A board - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_MB_A_PCI_H__ -#define __V850_MB_A_PCI_H__ - - -#define MB_A_PCI_MEM_ADDR GCS5_ADDR -#define MB_A_PCI_MEM_SIZE (GCS5_SIZE / 2) -#define MB_A_PCI_IO_ADDR (GCS5_ADDR + MB_A_PCI_MEM_SIZE) -#define MB_A_PCI_IO_SIZE (GCS5_SIZE / 2) -#define MB_A_PCI_REG_BASE_ADDR GCS6_ADDR - -#define MB_A_PCI_PCICR_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x4) -#define MB_A_PCI_PCICR (*(volatile u16 *)MB_A_PCI_PCICR_ADDR) -#define MB_A_PCI_PCISR_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x6) -#define MB_A_PCI_PCISR (*(volatile u16 *)MB_A_PCI_PCISR_ADDR) -#define MB_A_PCI_PCILTR_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xD) -#define MB_A_PCI_PCILTR (*(volatile u8 *)MB_A_PCI_PCILTR_ADDR) -#define MB_A_PCI_PCIBAR0_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x10) -#define MB_A_PCI_PCIBAR0 (*(volatile u32 *)MB_A_PCI_PCIBAR0_ADDR) -#define MB_A_PCI_PCIBAR1_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x14) -#define MB_A_PCI_PCIBAR1 (*(volatile u32 *)MB_A_PCI_PCIBAR1_ADDR) -#define MB_A_PCI_PCIBAR2_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x18) -#define MB_A_PCI_PCIBAR2 (*(volatile u32 *)MB_A_PCI_PCIBAR2_ADDR) -#define MB_A_PCI_VENDOR_ID_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x2C) -#define MB_A_PCI_VENDOR_ID (*(volatile u16 *)MB_A_PCI_VENDOR_ID_ADDR) -#define MB_A_PCI_DEVICE_ID_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x2E) -#define MB_A_PCI_DEVICE_ID (*(volatile u16 *)MB_A_PCI_DEVICE_ID_ADDR) -#define MB_A_PCI_DMRR_ADDR (MB_A_PCI_REG_BASE_ADDR + 0x9C) -#define MB_A_PCI_DMRR (*(volatile u32 *)MB_A_PCI_DMRR_ADDR) -#define MB_A_PCI_DMLBAM_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xA0) -#define MB_A_PCI_DMLBAM (*(volatile u32 *)MB_A_PCI_DMLBAM_ADDR) -#define MB_A_PCI_DMLBAI_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xA4) -#define MB_A_PCI_DMLBAI (*(volatile u32 *)MB_A_PCI_DMLBAI_ADDR) -#define MB_A_PCI_PCIPBAM_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xA8) -#define MB_A_PCI_PCIPBAM (*(volatile u32 *)MB_A_PCI_PCIPBAM_ADDR) -/* `PCI Configuration Address Register for Direct Master to PCI IO/CFG' */ -#define MB_A_PCI_DMCFGA_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xAC) -#define MB_A_PCI_DMCFGA (*(volatile u32 *)MB_A_PCI_DMCFGA_ADDR) -/* `PCI Permanent Configuration ID Register' */ -#define MB_A_PCI_PCIHIDR_ADDR (MB_A_PCI_REG_BASE_ADDR + 0xF0) -#define MB_A_PCI_PCIHIDR (*(volatile u32 *)MB_A_PCI_PCIHIDR_ADDR) - - -#endif /* __V850_MB_A_PCI_H__ */ diff --git a/include/asm-v850/rte_me2_cb.h b/include/asm-v850/rte_me2_cb.h deleted file mode 100644 index 9922c85c85a8..000000000000 --- a/include/asm-v850/rte_me2_cb.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * include/asm-v850/rte_me2_cb.h -- Midas labs RTE-V850E/ME2-CB board - * - * Copyright (C) 2001,02,03 NEC Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_RTE_ME2_CB_H__ -#define __V850_RTE_ME2_CB_H__ - -#include /* Common defs for Midas RTE-CB boards. */ - - -#define PLATFORM "rte-v850e/me2-cb" -#define PLATFORM_LONG "Midas lab RTE-V850E/ME2-CB" - -#define CPU_CLOCK_FREQ 150000000 /* 150MHz */ -#define FIXED_BOGOMIPS 50 - -/* 32MB of onbard SDRAM. */ -#define SDRAM_ADDR 0x00800000 -#define SDRAM_SIZE 0x02000000 /* 32MB */ - - -/* CPU addresses of GBUS memory spaces. */ -#define GCS0_ADDR 0x04000000 /* GCS0 - Common SRAM (2MB) */ -#define GCS0_SIZE 0x00800000 /* 8MB */ -#define GCS1_ADDR 0x04800000 /* GCS1 - Flash ROM (8MB) */ -#define GCS1_SIZE 0x00800000 /* 8MB */ -#define GCS2_ADDR 0x07000000 /* GCS2 - I/O registers */ -#define GCS2_SIZE 0x00800000 /* 8MB */ -#define GCS5_ADDR 0x08000000 /* GCS5 - PCI bus space */ -#define GCS5_SIZE 0x02000000 /* 32MB */ -#define GCS6_ADDR 0x07800000 /* GCS6 - PCI control registers */ -#define GCS6_SIZE 0x00800000 /* 8MB */ - - -/* For */ -#define PAGE_OFFSET SDRAM_ADDR - - -#ifdef CONFIG_ROM_KERNEL -/* Kernel is in ROM, starting at address 0. */ - -#define INTV_BASE 0 -#define ROOT_FS_IMAGE_RW 0 - -#else /* !CONFIG_ROM_KERNEL */ -/* Using RAM-kernel. Assume some sort of boot-loader got us loaded at - address 0. */ - -#define INTV_BASE 0 -#define ROOT_FS_IMAGE_RW 1 - -#endif /* CONFIG_ROM_KERNEL */ - - -/* Some misc. on-board devices. */ - -/* Seven-segment LED display (four digits). */ -#define LED_ADDR(n) (0x0FE02000 + (n)) -#define LED(n) (*(volatile unsigned char *)LED_ADDR(n)) -#define LED_NUM_DIGITS 4 - - -/* On-board PIC. */ - -#define CB_PIC_BASE_ADDR 0x0FE04000 - -#define CB_PIC_INT0M_ADDR (CB_PIC_BASE_ADDR + 0x00) -#define CB_PIC_INT0M (*(volatile u16 *)CB_PIC_INT0M_ADDR) -#define CB_PIC_INT1M_ADDR (CB_PIC_BASE_ADDR + 0x10) -#define CB_PIC_INT1M (*(volatile u16 *)CB_PIC_INT1M_ADDR) -#define CB_PIC_INTR_ADDR (CB_PIC_BASE_ADDR + 0x20) -#define CB_PIC_INTR (*(volatile u16 *)CB_PIC_INTR_ADDR) -#define CB_PIC_INTEN_ADDR (CB_PIC_BASE_ADDR + 0x30) -#define CB_PIC_INTEN (*(volatile u16 *)CB_PIC_INTEN_ADDR) - -#define CB_PIC_INT0EN 0x0001 -#define CB_PIC_INT1EN 0x0002 -#define CB_PIC_INT0SEL 0x0080 - -/* The PIC interrupts themselves. */ -#define CB_PIC_BASE_IRQ NUM_CPU_IRQS -#define IRQ_CB_PIC_NUM 10 - -/* Some specific CB_PIC interrupts. */ -#define IRQ_CB_EXTTM0 (CB_PIC_BASE_IRQ + 0) -#define IRQ_CB_EXTSIO (CB_PIC_BASE_IRQ + 1) -#define IRQ_CB_TOVER (CB_PIC_BASE_IRQ + 2) -#define IRQ_CB_GINT0 (CB_PIC_BASE_IRQ + 3) -#define IRQ_CB_USB (CB_PIC_BASE_IRQ + 4) -#define IRQ_CB_LANC (CB_PIC_BASE_IRQ + 5) -#define IRQ_CB_USB_VBUS_ON (CB_PIC_BASE_IRQ + 6) -#define IRQ_CB_USB_VBUS_OFF (CB_PIC_BASE_IRQ + 7) -#define IRQ_CB_EXTTM1 (CB_PIC_BASE_IRQ + 8) -#define IRQ_CB_EXTTM2 (CB_PIC_BASE_IRQ + 9) - -/* The GBUS GINT1 - GINT3 (note, not GINT0!) interrupts are connected to - the INTP65 - INTP67 pins on the CPU. These are shared among the GBUS - interrupts. */ -#define IRQ_GINT(n) IRQ_INTP((n) + 9) /* 0 is unused! */ -#define IRQ_GINT_NUM 4 /* 0 is unused! */ - -/* The shared interrupt line from the PIC is connected to CPU pin INTP23. */ -#define IRQ_CB_PIC IRQ_INTP(4) /* P23 */ - -/* Used by to derive NUM_MACH_IRQS. */ -#define NUM_RTE_CB_IRQS (NUM_CPU_IRQS + IRQ_CB_PIC_NUM) - - -#ifndef __ASSEMBLY__ -struct cb_pic_irq_init { - const char *name; /* name of interrupt type */ - - /* Range of kernel irq numbers for this type: - BASE, BASE+INTERVAL, ..., BASE+INTERVAL*NUM */ - unsigned base, num, interval; - - unsigned priority; /* interrupt priority to assign */ -}; -struct hw_interrupt_type; /* fwd decl */ - -/* Enable interrupt handling for interrupt IRQ. */ -extern void cb_pic_enable_irq (unsigned irq); -/* Disable interrupt handling for interrupt IRQ. Note that any interrupts - received while disabled will be delivered once the interrupt is enabled - again, unless they are explicitly cleared using `cb_pic_clear_pending_irq'. */ -extern void cb_pic_disable_irq (unsigned irq); -/* Initialize HW_IRQ_TYPES for PIC irqs described in array INITS (which is - terminated by an entry with the name field == 0). */ -extern void cb_pic_init_irq_types (struct cb_pic_irq_init *inits, - struct hw_interrupt_type *hw_irq_types); -/* Initialize PIC interrupts. */ -extern void cb_pic_init_irqs (void); -#endif /* __ASSEMBLY__ */ - - -/* TL16C550C on board UART see also asm/serial.h */ -#define CB_UART_BASE 0x0FE08000 -#define CB_UART_REG_GAP 0x10 -#define CB_UART_CLOCK 0x16000000 - -/* CompactFlash setting */ -#define CB_CF_BASE 0x0FE0C000 -#define CB_CF_CCR_ADDR (CB_CF_BASE+0x200) -#define CB_CF_CCR (*(volatile u8 *)CB_CF_CCR_ADDR) -#define CB_CF_REG0_ADDR (CB_CF_BASE+0x1000) -#define CB_CF_REG0 (*(volatile u16 *)CB_CF_REG0_ADDR) -#define CB_CF_STS0_ADDR (CB_CF_BASE+0x1004) -#define CB_CF_STS0 (*(volatile u16 *)CB_CF_STS0_ADDR) -#define CB_PCATA_BASE (CB_CF_BASE+0x800) -#define CB_IDE_BASE (CB_CF_BASE+0x9F0) -#define CB_IDE_CTRL (CB_CF_BASE+0xBF6) -#define CB_IDE_REG_OFFS 0x1 - - -/* SMSC LAN91C111 setting */ -#if defined(CONFIG_SMC91111) -#define CB_LANC_BASE 0x0FE10300 -#define CONFIG_SMC16BITONLY -#define ETH0_ADDR CB_LANC_BASE -#define ETH0_IRQ IRQ_CB_LANC -#endif /* CONFIG_SMC16BITONLY */ - - -#undef V850E_UART_PRE_CONFIGURE -#define V850E_UART_PRE_CONFIGURE rte_me2_cb_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void rte_me2_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif /* __ASSEMBLY__ */ - -/* This board supports RTS/CTS for the on-chip UART, but only for channel 0. */ - -/* CTS for UART channel 0 is pin P22 (bit 2 of port 2). */ -#define V850E_UART_CTS(chan) ((chan) == 0 ? !(ME2_PORT2_IO & 0x4) : 1) -/* RTS for UART channel 0 is pin P21 (bit 1 of port 2). */ -#define V850E_UART_SET_RTS(chan, val) \ - do { \ - if (chan == 0) { \ - unsigned old = ME2_PORT2_IO; \ - if (val) \ - ME2_PORT2_IO = old & ~0x2; \ - else \ - ME2_PORT2_IO = old | 0x2; \ - } \ - } while (0) - - -#ifndef __ASSEMBLY__ -extern void rte_me2_cb_init_irqs (void); -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_RTE_ME2_CB_H__ */ diff --git a/include/asm-v850/rte_nb85e_cb.h b/include/asm-v850/rte_nb85e_cb.h deleted file mode 100644 index f56591cad90a..000000000000 --- a/include/asm-v850/rte_nb85e_cb.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * include/asm-v850/rte_nb85e_cb.h -- Midas labs RTE-V850/NB85E-CB board - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_RTE_NB85E_CB_H__ -#define __V850_RTE_NB85E_CB_H__ - -#include /* Common defs for Midas RTE-CB boards. */ - - -#define PLATFORM "rte-v850e/nb85e-cb" -#define PLATFORM_LONG "Midas lab RTE-V850E/NB85E-CB" - -#define CPU_CLOCK_FREQ 50000000 /* 50MHz */ - -/* 1MB of onboard SRAM. Note that the monitor ROM uses parts of this - for its own purposes, so care must be taken. */ -#define SRAM_ADDR 0x03C00000 -#define SRAM_SIZE 0x00100000 /* 1MB */ - -/* 16MB of onbard SDRAM. */ -#define SDRAM_ADDR 0x01000000 -#define SDRAM_SIZE 0x01000000 /* 16MB */ - - -/* CPU addresses of GBUS memory spaces. */ -#define GCS0_ADDR 0x00400000 /* GCS0 - Common SRAM (2MB) */ -#define GCS0_SIZE 0x00400000 /* 4MB */ -#define GCS1_ADDR 0x02000000 /* GCS1 - Flash ROM (8MB) */ -#define GCS1_SIZE 0x00800000 /* 8MB */ -#define GCS2_ADDR 0x03900000 /* GCS2 - I/O registers */ -#define GCS2_SIZE 0x00080000 /* 512KB */ -#define GCS3_ADDR 0x02800000 /* GCS3 - EXT-bus: memory space */ -#define GCS3_SIZE 0x00800000 /* 8MB */ -#define GCS4_ADDR 0x03A00000 /* GCS4 - EXT-bus: I/O space */ -#define GCS4_SIZE 0x00200000 /* 2MB */ -#define GCS5_ADDR 0x00800000 /* GCS5 - PCI bus space */ -#define GCS5_SIZE 0x00800000 /* 8MB */ -#define GCS6_ADDR 0x03980000 /* GCS6 - PCI control registers */ -#define GCS6_SIZE 0x00010000 /* 64KB */ - - -/* The GBUS GINT0 - GINT3 interrupts are connected to CPU interrupts 10-12. - These are shared among the GBUS interrupts. */ -#define IRQ_GINT(n) (10 + (n)) -#define IRQ_GINT_NUM 3 - -/* Used by to derive NUM_MACH_IRQS. */ -#define NUM_RTE_CB_IRQS NUM_CPU_IRQS - - -#ifdef CONFIG_ROM_KERNEL -/* Kernel is in ROM, starting at address 0. */ - -#define INTV_BASE 0 - -#else /* !CONFIG_ROM_KERNEL */ -/* We're using the ROM monitor. */ - -/* The chip's real interrupt vectors are in ROM, but they jump to a - secondary interrupt vector table in RAM. */ -#define INTV_BASE 0x03CF8000 - -/* Scratch memory used by the ROM monitor, which shouldn't be used by - linux (except for the alternate interrupt vector area, defined - above). */ -#define MON_SCRATCH_ADDR 0x03CE8000 -#define MON_SCRATCH_SIZE 0x00018000 /* 96KB */ - -#endif /* CONFIG_ROM_KERNEL */ - - -/* Some misc. on-board devices. */ - -/* Seven-segment LED display (two digits). Write-only. */ -#define LED_ADDR(n) (0x03802000 + (n)) -#define LED(n) (*(volatile unsigned char *)LED_ADDR(n)) -#define LED_NUM_DIGITS 4 - - -/* Override the basic TEG UART pre-initialization so that we can - initialize extra stuff. */ -#undef V850E_UART_PRE_CONFIGURE /* should be defined by */ -#define V850E_UART_PRE_CONFIGURE rte_nb85e_cb_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void rte_nb85e_cb_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - -/* This board supports RTS/CTS for the on-chip UART. */ - -/* CTS is pin P00. */ -#define V850E_UART_CTS(chan) (! (TEG_PORT0_IO & 0x1)) -/* RTS is pin P02. */ -#define V850E_UART_SET_RTS(chan, val) \ - do { \ - unsigned old = TEG_PORT0_IO; \ - TEG_PORT0_IO = val ? (old & ~0x4) : (old | 0x4); \ - } while (0) - - -#endif /* __V850_RTE_NB85E_CB_H__ */ diff --git a/include/asm-v850/scatterlist.h b/include/asm-v850/scatterlist.h deleted file mode 100644 index 02d27b3fb061..000000000000 --- a/include/asm-v850/scatterlist.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * include/asm-v850/scatterlist.h - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SCATTERLIST_H__ -#define __V850_SCATTERLIST_H__ - -#include - -struct scatterlist { -#ifdef CONFIG_DEBUG_SG - unsigned long sg_magic; -#endif - unsigned long page_link; - unsigned offset; - dma_addr_t dma_address; - unsigned length; -}; - -#define ISA_DMA_THRESHOLD (~0UL) - -#endif /* __V850_SCATTERLIST_H__ */ diff --git a/include/asm-v850/sections.h b/include/asm-v850/sections.h deleted file mode 100644 index e0238253a0d0..000000000000 --- a/include/asm-v850/sections.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_SECTIONS_H__ -#define __V850_SECTIONS_H__ - -#include - -#endif /* __V850_SECTIONS_H__ */ diff --git a/include/asm-v850/segment.h b/include/asm-v850/segment.h deleted file mode 100644 index 5e2b15dcf3d9..000000000000 --- a/include/asm-v850/segment.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef __V850_SEGMENT_H__ -#define __V850_SEGMENT_H__ - - -#ifndef __ASSEMBLY__ - -typedef unsigned long mm_segment_t; /* domain register */ - -#endif /* !__ASSEMBLY__ */ - - -#define __KERNEL_CS 0x0 -#define __KERNEL_DS 0x0 - -#define __USER_CS 0x1 -#define __USER_DS 0x1 - -#define KERNEL_DS __KERNEL_DS -#define KERNEL_CS __KERNEL_CS -#define USER_DS __USER_DS -#define USER_CS __USER_CS - -#define segment_eq(a,b) ((a) == (b)) - -#define get_ds() (KERNEL_DS) -#define get_fs() (USER_DS) - -#define set_fs(seg) ((void)(seg)) - - -#define copy_segments(task, mm) ((void)((void)(task), (mm))) -#define release_segments(mm) ((void)(mm)) -#define forget_segments() ((void)0) - - -#endif /* __V850_SEGMENT_H__ */ diff --git a/include/asm-v850/semaphore.h b/include/asm-v850/semaphore.h deleted file mode 100644 index d9b2034ed1d2..000000000000 --- a/include/asm-v850/semaphore.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-v850/sembuf.h b/include/asm-v850/sembuf.h deleted file mode 100644 index 1622231a8b85..000000000000 --- a/include/asm-v850/sembuf.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __V850_SEMBUF_H__ -#define __V850_SEMBUF_H__ - -/* - * The semid64_ds structure for v850 architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 64-bit time_t to solve y2038 problem - * - 2 miscellaneous 32-bit values - */ - -struct semid64_ds { - struct ipc64_perm sem_perm; /* permissions .. see ipc.h */ - __kernel_time_t sem_otime; /* last semop time */ - unsigned long __unused1; - __kernel_time_t sem_ctime; /* last change time */ - unsigned long __unused2; - unsigned long sem_nsems; /* no. of semaphores in array */ - unsigned long __unused3; - unsigned long __unused4; -}; - -#endif /* __V850_SEMBUF_H__ */ diff --git a/include/asm-v850/serial.h b/include/asm-v850/serial.h deleted file mode 100644 index 36d8f4cbbf39..000000000000 --- a/include/asm-v850/serial.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 - * for more details. - * - * Copyright (C) 1999 by Ralf Baechle - * Copyright (C) 1999, 2000 Silicon Graphics, Inc. - */ - -#ifdef CONFIG_RTE_CB_ME2 - -#include - -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) - -#define irq_cannonicalize(x) (x) -#define BASE_BAUD 250000 /* (16MHz / (16 * 38400)) * 9600 */ -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, CB_UART_BASE, IRQ_CB_EXTSIO, STD_COM_FLAGS }, - -/* Redefine UART register offsets. */ -#undef UART_RX -#undef UART_TX -#undef UART_DLL -#undef UART_TRG -#undef UART_DLM -#undef UART_IER -#undef UART_FCTR -#undef UART_IIR -#undef UART_FCR -#undef UART_EFR -#undef UART_LCR -#undef UART_MCR -#undef UART_LSR -#undef UART_MSR -#undef UART_SCR -#undef UART_EMSR - -#define UART_RX (0 * CB_UART_REG_GAP) -#define UART_TX (0 * CB_UART_REG_GAP) -#define UART_DLL (0 * CB_UART_REG_GAP) -#define UART_TRG (0 * CB_UART_REG_GAP) -#define UART_DLM (1 * CB_UART_REG_GAP) -#define UART_IER (1 * CB_UART_REG_GAP) -#define UART_FCTR (1 * CB_UART_REG_GAP) -#define UART_IIR (2 * CB_UART_REG_GAP) -#define UART_FCR (2 * CB_UART_REG_GAP) -#define UART_EFR (2 * CB_UART_REG_GAP) -#define UART_LCR (3 * CB_UART_REG_GAP) -#define UART_MCR (4 * CB_UART_REG_GAP) -#define UART_LSR (5 * CB_UART_REG_GAP) -#define UART_MSR (6 * CB_UART_REG_GAP) -#define UART_SCR (7 * CB_UART_REG_GAP) -#define UART_EMSR (7 * CB_UART_REG_GAP) - -#endif /* CONFIG_RTE_CB_ME2 */ diff --git a/include/asm-v850/setup.h b/include/asm-v850/setup.h deleted file mode 100644 index c48a9b97d05b..000000000000 --- a/include/asm-v850/setup.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _V850_SETUP_H -#define _V850_SETUP_H - -#define COMMAND_LINE_SIZE 512 - -#endif /* __SETUP_H */ diff --git a/include/asm-v850/shmbuf.h b/include/asm-v850/shmbuf.h deleted file mode 100644 index 3d085c9c418e..000000000000 --- a/include/asm-v850/shmbuf.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __V850_SHMBUF_H__ -#define __V850_SHMBUF_H__ - -/* - * The shmid64_ds structure for v850 architecture. - * Note extra padding because this structure is passed back and forth - * between kernel and user space. - * - * Pad space is left for: - * - 64-bit time_t to solve y2038 problem - * - 2 miscellaneous 32-bit values - */ - -struct shmid64_ds { - struct ipc64_perm shm_perm; /* operation perms */ - size_t shm_segsz; /* size of segment (bytes) */ - __kernel_time_t shm_atime; /* last attach time */ - unsigned long __unused1; - __kernel_time_t shm_dtime; /* last detach time */ - unsigned long __unused2; - __kernel_time_t shm_ctime; /* last change time */ - unsigned long __unused3; - __kernel_pid_t shm_cpid; /* pid of creator */ - __kernel_pid_t shm_lpid; /* pid of last operator */ - unsigned long shm_nattch; /* no. of current attaches */ - unsigned long __unused4; - unsigned long __unused5; -}; - -struct shminfo64 { - unsigned long shmmax; - unsigned long shmmin; - unsigned long shmmni; - unsigned long shmseg; - unsigned long shmall; - unsigned long __unused1; - unsigned long __unused2; - unsigned long __unused3; - unsigned long __unused4; -}; - -#endif /* __V850_SHMBUF_H__ */ diff --git a/include/asm-v850/shmparam.h b/include/asm-v850/shmparam.h deleted file mode 100644 index 7dcb6739073e..000000000000 --- a/include/asm-v850/shmparam.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_SHMPARAM_H__ -#define __V850_SHMPARAM_H__ - -#define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ - -#endif /* __V850_SHMPARAM_H__ */ diff --git a/include/asm-v850/sigcontext.h b/include/asm-v850/sigcontext.h deleted file mode 100644 index e0890f6f4bc9..000000000000 --- a/include/asm-v850/sigcontext.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * include/asm-v850/sigcontext.h -- Signal contexts - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIGCONTEXT_H__ -#define __V850_SIGCONTEXT_H__ - -#include - -struct sigcontext -{ - struct pt_regs regs; - unsigned long oldmask; -}; - -#endif /* __V850_SIGCONTEXT_H__ */ diff --git a/include/asm-v850/siginfo.h b/include/asm-v850/siginfo.h deleted file mode 100644 index 7eb94703dce0..000000000000 --- a/include/asm-v850/siginfo.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_SIGINFO_H__ -#define __V850_SIGINFO_H__ - -#include - -#endif /* __V850_SIGINFO_H__ */ diff --git a/include/asm-v850/signal.h b/include/asm-v850/signal.h deleted file mode 100644 index a38df0834bbf..000000000000 --- a/include/asm-v850/signal.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef __V850_SIGNAL_H__ -#define __V850_SIGNAL_H__ - -#include - -/* Avoid too many header ordering problems. */ -struct siginfo; - - -#ifdef __KERNEL__ - -/* Most things should be clean enough to redefine this at will, if care - is taken to make libc match. */ -#define _NSIG 64 -#define _NSIG_BPW 32 -#define _NSIG_WORDS (_NSIG / _NSIG_BPW) - -typedef unsigned long old_sigset_t; /* at least 32 bits */ - -typedef struct { - unsigned long sig[_NSIG_WORDS]; -} sigset_t; - -#else /* !__KERNEL__ */ - -/* Here we must cater to libcs that poke about in kernel headers. */ - -#define NSIG 32 -typedef unsigned long sigset_t; - -#endif /* __KERNEL__ */ - - -#define SIGHUP 1 -#define SIGINT 2 -#define SIGQUIT 3 -#define SIGILL 4 -#define SIGTRAP 5 -#define SIGABRT 6 -#define SIGIOT 6 -#define SIGBUS 7 -#define SIGFPE 8 -#define SIGKILL 9 -#define SIGUSR1 10 -#define SIGSEGV 11 -#define SIGUSR2 12 -#define SIGPIPE 13 -#define SIGALRM 14 -#define SIGTERM 15 -#define SIGSTKFLT 16 -#define SIGCHLD 17 -#define SIGCONT 18 -#define SIGSTOP 19 -#define SIGTSTP 20 -#define SIGTTIN 21 -#define SIGTTOU 22 -#define SIGURG 23 -#define SIGXCPU 24 -#define SIGXFSZ 25 -#define SIGVTALRM 26 -#define SIGPROF 27 -#define SIGWINCH 28 -#define SIGIO 29 -#define SIGPOLL SIGIO -/* -#define SIGLOST 29 -*/ -#define SIGPWR 30 -#define SIGSYS 31 -#define SIGUNUSED 31 - -/* These should not be considered constants from userland. */ -#define SIGRTMIN 32 -#define SIGRTMAX _NSIG - -/* - * SA_FLAGS values: - * - * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_RESTART flag to get restarting signals (which were the default long ago) - * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. - * SA_RESETHAND clears the handler when the signal is delivered. - * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. - * SA_NODEFER prevents the current signal from being masked in the handler. - * - * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single - * Unix names RESETHAND and NODEFER respectively. - */ -#define SA_NOCLDSTOP 0x00000001 -#define SA_NOCLDWAIT 0x00000002 -#define SA_SIGINFO 0x00000004 -#define SA_ONSTACK 0x08000000 -#define SA_RESTART 0x10000000 -#define SA_NODEFER 0x40000000 -#define SA_RESETHAND 0x80000000 - -#define SA_NOMASK SA_NODEFER -#define SA_ONESHOT SA_RESETHAND - -#define SA_RESTORER 0x04000000 - -/* - * sigaltstack controls - */ -#define SS_ONSTACK 1 -#define SS_DISABLE 2 - -#define MINSIGSTKSZ 2048 -#define SIGSTKSZ 8192 - -#include - -#ifdef __KERNEL__ - -struct old_sigaction { - __sighandler_t sa_handler; - old_sigset_t sa_mask; - unsigned long sa_flags; - void (*sa_restorer)(void); -}; - -struct sigaction { - __sighandler_t sa_handler; - unsigned long sa_flags; - void (*sa_restorer)(void); - sigset_t sa_mask; /* mask last for extensibility */ -}; - -struct k_sigaction { - struct sigaction sa; -}; - -#else /* !__KERNEL__ */ - -/* Here we must cater to libcs that poke about in kernel headers. */ - -struct sigaction { - union { - __sighandler_t _sa_handler; - void (*_sa_sigaction)(int, struct siginfo *, void *); - } _u; - sigset_t sa_mask; - unsigned long sa_flags; - void (*sa_restorer)(void); -}; - -#define sa_handler _u._sa_handler -#define sa_sigaction _u._sa_sigaction - -#endif /* __KERNEL__ */ - - -typedef struct sigaltstack { - void *ss_sp; - int ss_flags; - size_t ss_size; -} stack_t; - -#ifdef __KERNEL__ - -#include -#undef __HAVE_ARCH_SIG_BITOPS - -#define ptrace_signal_deliver(regs, cookie) do { } while (0) - -#endif /* __KERNEL__ */ - -#endif /* __V850_SIGNAL_H__ */ diff --git a/include/asm-v850/sim.h b/include/asm-v850/sim.h deleted file mode 100644 index 026932d476cd..000000000000 --- a/include/asm-v850/sim.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * include/asm-v850/sim.h -- Machine-dependent defs for GDB v850e simulator - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIM_H__ -#define __V850_SIM_H__ - - -#define CPU_ARCH "v850e" -#define CPU_MODEL "v850e" -#define CPU_MODEL_LONG "NEC V850E" -#define PLATFORM "gdb/v850e" -#define PLATFORM_LONG "GDB V850E simulator" - - -/* We use a weird value for RAM, not just 0, for testing purposes. - These must match the values used in the linker script. */ -#define RAM_ADDR 0x8F000000 -#define RAM_SIZE 0x03000000 - - -/* For */ -#define PAGE_OFFSET RAM_ADDR - - -/* For */ -/* `R0 RAM', used for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. On real - processors, this usually is on-chip RAM, but here we just - choose an arbitrary address that meets the above constraint. */ -#define R0_RAM_ADDR 0xFFFFF000 - - -/* For */ -#define NUM_CPU_IRQS 6 - - -#endif /* __V850_SIM_H__ */ diff --git a/include/asm-v850/sim85e2.h b/include/asm-v850/sim85e2.h deleted file mode 100644 index 8b4d6974066c..000000000000 --- a/include/asm-v850/sim85e2.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * include/asm-v850/sim85e2.h -- Machine-dependent defs for - * V850E2 RTL simulator - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIM85E2_H__ -#define __V850_SIM85E2_H__ - - -#include /* Based on V850E2 core. */ - - -/* Various memory areas supported by the simulator. - These should match the corresponding definitions in the linker script. */ - -/* `instruction RAM'; instruction fetches are much faster from IRAM than - from DRAM. */ -#define IRAM_ADDR 0 -#define IRAM_SIZE 0x00100000 /* 1MB */ -/* `data RAM', below and contiguous with the I/O space. - Data fetches are much faster from DRAM than from IRAM. */ -#define DRAM_ADDR 0xfff00000 -#define DRAM_SIZE 0x000ff000 /* 1020KB */ -/* `external ram'. Unlike the above RAM areas, this memory is cached, - so both instruction and data fetches should be (mostly) fast -- - however, currently only write-through caching is supported, so writes - to ERAM will be slow. */ -#define ERAM_ADDR 0x00100000 -#define ERAM_SIZE 0x07f00000 /* 127MB (max) */ -/* Dynamic RAM; uses memory controller. */ -#define SDRAM_ADDR 0x10000000 -#define SDRAM_SIZE 0x01000000 /* 16MB */ - - -/* Simulator specific control registers. */ -/* NOTHAL controls whether the simulator will stop at a `halt' insn. */ -#define SIM85E2_NOTHAL_ADDR 0xffffff22 -#define SIM85E2_NOTHAL (*(volatile u8 *)SIM85E2_NOTHAL_ADDR) -/* The simulator will stop N cycles after N is written to SIMFIN. */ -#define SIM85E2_SIMFIN_ADDR 0xffffff24 -#define SIM85E2_SIMFIN (*(volatile u16 *)SIM85E2_SIMFIN_ADDR) - - -/* For */ -#define NUM_CPU_IRQS 64 - - -/* For */ -#define PAGE_OFFSET SDRAM_ADDR - - -/* For */ -/* `R0 RAM', used for a few miscellaneous variables that must be accessible - using a load instruction relative to R0. The sim85e2 simulator - actually puts 1020K of RAM from FFF00000 to FFFFF000, so we arbitarily - choose a small portion at the end of that. */ -#define R0_RAM_ADDR 0xFFFFE000 - - -#endif /* __V850_SIM85E2_H__ */ diff --git a/include/asm-v850/sim85e2c.h b/include/asm-v850/sim85e2c.h deleted file mode 100644 index eee543ff3af8..000000000000 --- a/include/asm-v850/sim85e2c.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * include/asm-v850/sim85e2c.h -- Machine-dependent defs for - * V850E2 RTL simulator - * - * Copyright (C) 2002 NEC Corporation - * Copyright (C) 2002 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIM85E2C_H__ -#define __V850_SIM85E2C_H__ - -/* Use generic sim85e2 settings, other than the various names. */ -#include - -#define CPU_MODEL "v850e2" -#define CPU_MODEL_LONG "NEC V850E2" -#define PLATFORM "sim85e2c" -#define PLATFORM_LONG "SIM85E2C V850E2 simulator" - -#endif /* __V850_SIM85E2C_H__ */ diff --git a/include/asm-v850/sim85e2s.h b/include/asm-v850/sim85e2s.h deleted file mode 100644 index ee066d5d3c51..000000000000 --- a/include/asm-v850/sim85e2s.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * include/asm-v850/sim85e2s.h -- Machine-dependent defs for - * V850E2 RTL simulator - * - * Copyright (C) 2003 NEC Electronics Corporation - * Copyright (C) 2003 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIM85E2S_H__ -#define __V850_SIM85E2S_H__ - -#include /* Use generic sim85e2 settings. */ -#if 0 -#include /* + cache */ -#endif - -#define CPU_MODEL "v850e2" -#define CPU_MODEL_LONG "NEC V850E2" -#define PLATFORM "sim85e2s" -#define PLATFORM_LONG "SIM85E2S V850E2 simulator" - -#endif /* __V850_SIM85E2S_H__ */ diff --git a/include/asm-v850/simsyscall.h b/include/asm-v850/simsyscall.h deleted file mode 100644 index 4a19d5ae9d17..000000000000 --- a/include/asm-v850/simsyscall.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * include/asm-v850/simsyscall.h -- `System calls' under the v850e emulator - * - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SIMSYSCALL_H__ -#define __V850_SIMSYSCALL_H__ - -#define V850_SIM_SYS_exit(a...) V850_SIM_SYSCALL_1 (1 , ##a) -#define V850_SIM_SYS_fork(a...) V850_SIM_SYSCALL_0 (2 , ##a) -#define V850_SIM_SYS_read(a...) V850_SIM_SYSCALL_3 (3 , ##a) -#define V850_SIM_SYS_write(a...) V850_SIM_SYSCALL_3 (4 , ##a) -#define V850_SIM_SYS_open(a...) V850_SIM_SYSCALL_2 (5 , ##a) -#define V850_SIM_SYS_close(a...) V850_SIM_SYSCALL_1 (6 , ##a) -#define V850_SIM_SYS_wait4(a...) V850_SIM_SYSCALL_4 (7 , ##a) -/* #define V850_SIM_SYS_creat(a...) V850_SIM_SYSCALL_1 (8 , ##a) */ -/* #define V850_SIM_SYS_link(a...) V850_SIM_SYSCALL_1 (9 , ##a) */ -/* #define V850_SIM_SYS_unlink(a...) V850_SIM_SYSCALL_1 (10 , ##a) */ -#define V850_SIM_SYS_execv(a...) V850_SIM_SYSCALL_2 (11 , ##a) -/* #define V850_SIM_SYS_chdir(a...) V850_SIM_SYSCALL_1 (12 , ##a) */ -/* #define V850_SIM_SYS_mknod(a...) V850_SIM_SYSCALL_1 (14 , ##a) */ -#define V850_SIM_SYS_chmod(a...) V850_SIM_SYSCALL_2 (15 , ##a) -#define V850_SIM_SYS_chown(a...) V850_SIM_SYSCALL_2 (16 , ##a) -#define V850_SIM_SYS_lseek(a...) V850_SIM_SYSCALL_3 (19 , ##a) -/* #define V850_SIM_SYS_getpid(a...) V850_SIM_SYSCALL_1 (20 , ##a) */ -/* #define V850_SIM_SYS_isatty(a...) V850_SIM_SYSCALL_1 (21 , ##a) */ -/* #define V850_SIM_SYS_fstat(a...) V850_SIM_SYSCALL_1 (22 , ##a) */ -#define V850_SIM_SYS_time(a...) V850_SIM_SYSCALL_1 (23 , ##a) -#define V850_SIM_SYS_poll(a...) V850_SIM_SYSCALL_3 (24 , ##a) -#define V850_SIM_SYS_stat(a...) V850_SIM_SYSCALL_2 (38 , ##a) -#define V850_SIM_SYS_pipe(a...) V850_SIM_SYSCALL_1 (42 , ##a) -#define V850_SIM_SYS_times(a...) V850_SIM_SYSCALL_1 (43 , ##a) -#define V850_SIM_SYS_execve(a...) V850_SIM_SYSCALL_3 (59 , ##a) -#define V850_SIM_SYS_gettimeofday(a...) V850_SIM_SYSCALL_2 (116 , ##a) -/* #define V850_SIM_SYS_utime(a...) V850_SIM_SYSCALL_2 (201 , ##a) */ -/* #define V850_SIM_SYS_wait(a...) V850_SIM_SYSCALL_1 (202 , ##a) */ - -#define V850_SIM_SYS_make_raw(a...) V850_SIM_SYSCALL_1 (1024 , ##a) - - -#define V850_SIM_SYSCALL_0(_call) \ -({ \ - register int call __asm__ ("r6") = _call; \ - register int rval __asm__ ("r10"); \ - __asm__ __volatile__ ("trap 31" \ - : "=r" (rval) \ - : "r" (call) \ - : "r11", "memory"); \ - rval; \ -}) -#define V850_SIM_SYSCALL_1(_call, _arg0) \ -({ \ - register int call __asm__ ("r6") = _call; \ - register long arg0 __asm__ ("r7") = (long)_arg0; \ - register int rval __asm__ ("r10"); \ - __asm__ __volatile__ ("trap 31" \ - : "=r" (rval) \ - : "r" (call), "r" (arg0) \ - : "r11", "memory"); \ - rval; \ -}) -#define V850_SIM_SYSCALL_2(_call, _arg0, _arg1) \ -({ \ - register int call __asm__ ("r6") = _call; \ - register long arg0 __asm__ ("r7") = (long)_arg0; \ - register long arg1 __asm__ ("r8") = (long)_arg1; \ - register int rval __asm__ ("r10"); \ - __asm__ __volatile__ ("trap 31" \ - : "=r" (rval) \ - : "r" (call), "r" (arg0), "r" (arg1) \ - : "r11", "memory"); \ - rval; \ -}) -#define V850_SIM_SYSCALL_3(_call, _arg0, _arg1, _arg2) \ -({ \ - register int call __asm__ ("r6") = _call; \ - register long arg0 __asm__ ("r7") = (long)_arg0; \ - register long arg1 __asm__ ("r8") = (long)_arg1; \ - register long arg2 __asm__ ("r9") = (long)_arg2; \ - register int rval __asm__ ("r10"); \ - __asm__ __volatile__ ("trap 31" \ - : "=r" (rval) \ - : "r" (call), "r" (arg0), "r" (arg1), "r" (arg2)\ - : "r11", "memory"); \ - rval; \ -}) - -#define V850_SIM_SYSCALL(call, args...) \ - V850_SIM_SYS_##call (args) - -#endif /* __V850_SIMSYSCALL_H__ */ diff --git a/include/asm-v850/socket.h b/include/asm-v850/socket.h deleted file mode 100644 index e199a2bf12aa..000000000000 --- a/include/asm-v850/socket.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef __V850_SOCKET_H__ -#define __V850_SOCKET_H__ - -#include - -/* For setsockoptions(2) */ -#define SOL_SOCKET 1 - -#define SO_DEBUG 1 -#define SO_REUSEADDR 2 -#define SO_TYPE 3 -#define SO_ERROR 4 -#define SO_DONTROUTE 5 -#define SO_BROADCAST 6 -#define SO_SNDBUF 7 -#define SO_RCVBUF 8 -#define SO_SNDBUFFORCE 32 -#define SO_RCVBUFFORCE 33 -#define SO_KEEPALIVE 9 -#define SO_OOBINLINE 10 -#define SO_NO_CHECK 11 -#define SO_PRIORITY 12 -#define SO_LINGER 13 -#define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ -#define SO_PASSCRED 16 -#define SO_PEERCRED 17 -#define SO_RCVLOWAT 18 -#define SO_SNDLOWAT 19 -#define SO_RCVTIMEO 20 -#define SO_SNDTIMEO 21 - -/* Security levels - as per NRL IPv6 - don't actually do anything */ -#define SO_SECURITY_AUTHENTICATION 22 -#define SO_SECURITY_ENCRYPTION_TRANSPORT 23 -#define SO_SECURITY_ENCRYPTION_NETWORK 24 - -#define SO_BINDTODEVICE 25 - -/* Socket filtering */ -#define SO_ATTACH_FILTER 26 -#define SO_DETACH_FILTER 27 - -#define SO_PEERNAME 28 -#define SO_TIMESTAMP 29 -#define SCM_TIMESTAMP SO_TIMESTAMP - -#define SO_ACCEPTCONN 30 - -#define SO_PEERSEC 31 -#define SO_PASSSEC 34 -#define SO_TIMESTAMPNS 35 -#define SCM_TIMESTAMPNS SO_TIMESTAMPNS - -#define SO_MARK 36 - -#endif /* __V850_SOCKET_H__ */ diff --git a/include/asm-v850/sockios.h b/include/asm-v850/sockios.h deleted file mode 100644 index 823e106e6cd0..000000000000 --- a/include/asm-v850/sockios.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __V850_SOCKIOS_H__ -#define __V850_SOCKIOS_H__ - -/* Socket-level I/O control calls. */ -#define FIOSETOWN 0x8901 -#define SIOCSPGRP 0x8902 -#define FIOGETOWN 0x8903 -#define SIOCGPGRP 0x8904 -#define SIOCATMARK 0x8905 -#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ -#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ - -#endif /* __V850_SOCKIOS_H__ */ diff --git a/include/asm-v850/stat.h b/include/asm-v850/stat.h deleted file mode 100644 index c68c60d06e2f..000000000000 --- a/include/asm-v850/stat.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * include/asm-v850/stat.h -- v850 stat structure - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_STAT_H__ -#define __V850_STAT_H__ - -#include - -struct stat { - unsigned int st_dev; - unsigned long st_ino; - unsigned int st_mode; - unsigned int st_nlink; - unsigned int st_uid; - unsigned int st_gid; - unsigned int st_rdev; - long st_size; - unsigned long st_blksize; - unsigned long st_blocks; - unsigned long st_atime; - unsigned long __unused1; - unsigned long st_mtime; - unsigned long __unused2; - unsigned long st_ctime; - unsigned long __unused3; - unsigned long __unused4; - unsigned long __unused5; -}; - -struct stat64 { - unsigned long long st_dev; - unsigned long __unused1; - - unsigned long long st_ino; - - unsigned int st_mode; - unsigned int st_nlink; - - unsigned int st_uid; - unsigned int st_gid; - - unsigned long long st_rdev; - unsigned long __unused3; - - long long st_size; - unsigned long st_blksize; - - unsigned long st_blocks; /* No. of 512-byte blocks allocated */ - unsigned long __unused4; /* future possible st_blocks high bits */ - - unsigned long st_atime; - unsigned long st_atime_nsec; - - unsigned long st_mtime; - unsigned long st_mtime_nsec; - - unsigned long st_ctime; - unsigned long st_ctime_nsec; - - unsigned long __unused8; -}; - -#endif /* __V850_STAT_H__ */ diff --git a/include/asm-v850/statfs.h b/include/asm-v850/statfs.h deleted file mode 100644 index ea1596607f26..000000000000 --- a/include/asm-v850/statfs.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_STATFS_H__ -#define __V850_STATFS_H__ - -#include - -#endif /* __V850_STATFS_H__ */ diff --git a/include/asm-v850/string.h b/include/asm-v850/string.h deleted file mode 100644 index 478e234789d6..000000000000 --- a/include/asm-v850/string.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * include/asm-v850/string.h -- Architecture specific string routines - * - * Copyright (C) 2001,02 NEC Corporation - * Copyright (C) 2001,02 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_STRING_H__ -#define __V850_STRING_H__ - -#define __HAVE_ARCH_MEMCPY -#define __HAVE_ARCH_MEMSET -#define __HAVE_ARCH_MEMMOVE - -extern void *memcpy (void *, const void *, __kernel_size_t); -extern void *memset (void *, int, __kernel_size_t); -extern void *memmove (void *, const void *, __kernel_size_t); - -#endif /* __V850_STRING_H__ */ diff --git a/include/asm-v850/system.h b/include/asm-v850/system.h deleted file mode 100644 index 7daf1fdee119..000000000000 --- a/include/asm-v850/system.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * include/asm-v850/system.h -- Low-level interrupt/thread ops - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_SYSTEM_H__ -#define __V850_SYSTEM_H__ - -#include -#include - - -/* - * switch_to(n) should switch tasks to task ptr, first checking that - * ptr isn't the current task, in which case it does nothing. - */ -struct thread_struct; -extern void *switch_thread (struct thread_struct *last, - struct thread_struct *next); -#define switch_to(prev,next,last) \ - do { \ - if (prev != next) { \ - (last) = switch_thread (&prev->thread, &next->thread); \ - } \ - } while (0) - - -/* Enable/disable interrupts. */ -#define local_irq_enable() __asm__ __volatile__ ("ei") -#define local_irq_disable() __asm__ __volatile__ ("di") - -#define local_save_flags(flags) \ - __asm__ __volatile__ ("stsr %1, %0" : "=r" (flags) : "i" (SR_PSW)) -#define local_restore_flags(flags) \ - __asm__ __volatile__ ("ldsr %0, %1" :: "r" (flags), "i" (SR_PSW)) - -/* For spinlocks etc */ -#define local_irq_save(flags) \ - do { local_save_flags (flags); local_irq_disable (); } while (0) -#define local_irq_restore(flags) \ - local_restore_flags (flags); - - -static inline int irqs_disabled (void) -{ - unsigned flags; - local_save_flags (flags); - return !!(flags & 0x20); -} - - -/* - * Force strict CPU ordering. - * Not really required on v850... - */ -#define nop() __asm__ __volatile__ ("nop") -#define mb() __asm__ __volatile__ ("" ::: "memory") -#define rmb() mb () -#define wmb() mb () -#define read_barrier_depends() ((void)0) -#define set_mb(var, value) do { xchg (&var, value); } while (0) - -#define smp_mb() mb () -#define smp_rmb() rmb () -#define smp_wmb() wmb () -#define smp_read_barrier_depends() read_barrier_depends() - -#define xchg(ptr, with) \ - ((__typeof__ (*(ptr)))__xchg ((unsigned long)(with), (ptr), sizeof (*(ptr)))) - -static inline unsigned long __xchg (unsigned long with, - __volatile__ void *ptr, int size) -{ - unsigned long tmp, flags; - - local_irq_save (flags); - - switch (size) { - case 1: - tmp = *(unsigned char *)ptr; - *(unsigned char *)ptr = with; - break; - case 2: - tmp = *(unsigned short *)ptr; - *(unsigned short *)ptr = with; - break; - case 4: - tmp = *(unsigned long *)ptr; - *(unsigned long *)ptr = with; - break; - } - - local_irq_restore (flags); - - return tmp; -} - -#include - -/* - * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make - * them available. - */ -#define cmpxchg_local(ptr, o, n) \ - ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ - (unsigned long)(n), sizeof(*(ptr)))) -#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) - -#ifndef CONFIG_SMP -#include -#endif - -#define arch_align_stack(x) (x) - -#endif /* __V850_SYSTEM_H__ */ diff --git a/include/asm-v850/teg.h b/include/asm-v850/teg.h deleted file mode 100644 index acc8c7d95329..000000000000 --- a/include/asm-v850/teg.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * include/asm-v850/teg.h -- NB85E-TEG cpu chip - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_TEG_H__ -#define __V850_TEG_H__ - - -/* The TEG uses the V850E cpu core. */ -#include -#include - - -#define CPU_MODEL "v850e/nb85e-teg" -#define CPU_MODEL_LONG "NEC V850E/NB85E TEG" - - -/* For */ -/* We use on-chip RAM, for a few miscellaneous variables that must be - accessible using a load instruction relative to R0. On the NB85E/TEG, - There's 60KB of iRAM starting at 0xFFFF0000, however we need the base - address to be addressable by a 16-bit signed offset, so we only use the - second half of it starting from 0xFFFF8000. */ -#define R0_RAM_ADDR 0xFFFF8000 - - -/* Hardware-specific interrupt numbers (in the kernel IRQ namespace). - Some of these are parameterized even though there's only a single - interrupt, for compatibility with some generic code that works on other - processor models. */ -#define IRQ_INTCMD(n) 6 /* interval timer interrupt */ -#define IRQ_INTCMD_NUM 1 -#define IRQ_INTSER(n) 16 /* UART reception error */ -#define IRQ_INTSER_NUM 1 -#define IRQ_INTSR(n) 17 /* UART reception completion */ -#define IRQ_INTSR_NUM 1 -#define IRQ_INTST(n) 18 /* UART transmission completion */ -#define IRQ_INTST_NUM 1 - -/* For */ -#define NUM_CPU_IRQS 64 - - -/* TEG UART details. */ -#define V850E_UART_BASE_ADDR(n) (0xFFFFF600 + 0x10 * (n)) -#define V850E_UART_ASIM_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x0) -#define V850E_UART_ASIS_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x2) -#define V850E_UART_ASIF_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x4) -#define V850E_UART_CKSR_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x6) -#define V850E_UART_BRGC_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x8) -#define V850E_UART_TXB_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0xA) -#define V850E_UART_RXB_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0xC) -#define V850E_UART_NUM_CHANNELS 1 -#define V850E_UART_BASE_FREQ CPU_CLOCK_FREQ -/* This is a function that gets called before configuring the UART. */ -#define V850E_UART_PRE_CONFIGURE teg_uart_pre_configure -#ifndef __ASSEMBLY__ -extern void teg_uart_pre_configure (unsigned chan, - unsigned cflags, unsigned baud); -#endif - - -/* The TEG RTPU. */ -#define V850E_RTPU_BASE_ADDR 0xFFFFF210 - - -/* TEG series timer D details. */ -#define V850E_TIMER_D_BASE_ADDR 0xFFFFF210 -#define V850E_TIMER_D_TMCD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x0) -#define V850E_TIMER_D_TMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x4) -#define V850E_TIMER_D_CMD_BASE_ADDR (V850E_TIMER_D_BASE_ADDR + 0x8) -#define V850E_TIMER_D_BASE_FREQ CPU_CLOCK_FREQ - - -/* `Interrupt Source Select' control register. */ -#define TEG_ISS_ADDR 0xFFFFF7FA -#define TEG_ISS (*(volatile u8 *)TEG_ISS_ADDR) - -/* Port 0 I/O register (bits 0-3 used). */ -#define TEG_PORT0_IO_ADDR 0xFFFFF7F2 -#define TEG_PORT0_IO (*(volatile u8 *)TEG_PORT0_IO_ADDR) -/* Port 0 control register (bits 0-3 control mode, 0 = output, 1 = input). */ -#define TEG_PORT0_PM_ADDR 0xFFFFF7F4 -#define TEG_PORT0_PM (*(volatile u8 *)TEG_PORT0_PM_ADDR) - - -#ifndef __ASSEMBLY__ -extern void teg_init_irqs (void); -#endif - - -#endif /* __V850_TEG_H__ */ diff --git a/include/asm-v850/termbits.h b/include/asm-v850/termbits.h deleted file mode 100644 index 295d7bf69451..000000000000 --- a/include/asm-v850/termbits.h +++ /dev/null @@ -1,200 +0,0 @@ -#ifndef __V850_TERMBITS_H__ -#define __V850_TERMBITS_H__ - -#include - -typedef unsigned char cc_t; -typedef unsigned int speed_t; -typedef unsigned int tcflag_t; - -#define NCCS 19 -struct termios { - tcflag_t c_iflag; /* input mode flags */ - tcflag_t c_oflag; /* output mode flags */ - tcflag_t c_cflag; /* control mode flags */ - tcflag_t c_lflag; /* local mode flags */ - cc_t c_line; /* line discipline */ - cc_t c_cc[NCCS]; /* control characters */ -}; - -struct termios2 { - tcflag_t c_iflag; /* input mode flags */ - tcflag_t c_oflag; /* output mode flags */ - tcflag_t c_cflag; /* control mode flags */ - tcflag_t c_lflag; /* local mode flags */ - cc_t c_line; /* line discipline */ - cc_t c_cc[NCCS]; /* control characters */ - speed_t c_ispeed; /* input speed */ - speed_t c_ospeed; /* output speed */ -}; - -struct ktermios { - tcflag_t c_iflag; /* input mode flags */ - tcflag_t c_oflag; /* output mode flags */ - tcflag_t c_cflag; /* control mode flags */ - tcflag_t c_lflag; /* local mode flags */ - cc_t c_line; /* line discipline */ - cc_t c_cc[NCCS]; /* control characters */ - speed_t c_ispeed; /* input speed */ - speed_t c_ospeed; /* output speed */ -}; - -/* c_cc characters */ -#define VINTR 0 -#define VQUIT 1 -#define VERASE 2 -#define VKILL 3 -#define VEOF 4 -#define VTIME 5 -#define VMIN 6 -#define VSWTC 7 -#define VSTART 8 -#define VSTOP 9 -#define VSUSP 10 -#define VEOL 11 -#define VREPRINT 12 -#define VDISCARD 13 -#define VWERASE 14 -#define VLNEXT 15 -#define VEOL2 16 - - -/* c_iflag bits */ -#define IGNBRK 0000001 -#define BRKINT 0000002 -#define IGNPAR 0000004 -#define PARMRK 0000010 -#define INPCK 0000020 -#define ISTRIP 0000040 -#define INLCR 0000100 -#define IGNCR 0000200 -#define ICRNL 0000400 -#define IUCLC 0001000 -#define IXON 0002000 -#define IXANY 0004000 -#define IXOFF 0010000 -#define IMAXBEL 0020000 -#define IUTF8 0040000 - -/* c_oflag bits */ -#define OPOST 0000001 -#define OLCUC 0000002 -#define ONLCR 0000004 -#define OCRNL 0000010 -#define ONOCR 0000020 -#define ONLRET 0000040 -#define OFILL 0000100 -#define OFDEL 0000200 -#define NLDLY 0000400 -#define NL0 0000000 -#define NL1 0000400 -#define CRDLY 0003000 -#define CR0 0000000 -#define CR1 0001000 -#define CR2 0002000 -#define CR3 0003000 -#define TABDLY 0014000 -#define TAB0 0000000 -#define TAB1 0004000 -#define TAB2 0010000 -#define TAB3 0014000 -#define XTABS 0014000 -#define BSDLY 0020000 -#define BS0 0000000 -#define BS1 0020000 -#define VTDLY 0040000 -#define VT0 0000000 -#define VT1 0040000 -#define FFDLY 0100000 -#define FF0 0000000 -#define FF1 0100000 - -/* c_cflag bit meaning */ -#define CBAUD 0010017 -#define B0 0000000 /* hang up */ -#define B50 0000001 -#define B75 0000002 -#define B110 0000003 -#define B134 0000004 -#define B150 0000005 -#define B200 0000006 -#define B300 0000007 -#define B600 0000010 -#define B1200 0000011 -#define B1800 0000012 -#define B2400 0000013 -#define B4800 0000014 -#define B9600 0000015 -#define B19200 0000016 -#define B38400 0000017 -#define EXTA B19200 -#define EXTB B38400 -#define CSIZE 0000060 -#define CS5 0000000 -#define CS6 0000020 -#define CS7 0000040 -#define CS8 0000060 -#define CSTOPB 0000100 -#define CREAD 0000200 -#define PARENB 0000400 -#define PARODD 0001000 -#define HUPCL 0002000 -#define CLOCAL 0004000 -#define CBAUDEX 0010000 -#define BOTHER 0010000 -#define B57600 0010001 -#define B115200 0010002 -#define B230400 0010003 -#define B460800 0010004 -#define B500000 0010005 -#define B576000 0010006 -#define B921600 0010007 -#define B1000000 0010010 -#define B1152000 0010011 -#define B1500000 0010012 -#define B2000000 0010013 -#define B2500000 0010014 -#define B3000000 0010015 -#define B3500000 0010016 -#define B4000000 0010017 -#define CIBAUD 002003600000 /* input baud rate */ -#define CMSPAR 010000000000 /* mark or space (stick) parity */ -#define CRTSCTS 020000000000 /* flow control */ - -#define IBSHIFT 16 /* Shifr from CBAUD to CIBAUD */ - -/* c_lflag bits */ -#define ISIG 0000001 -#define ICANON 0000002 -#define XCASE 0000004 -#define ECHO 0000010 -#define ECHOE 0000020 -#define ECHOK 0000040 -#define ECHONL 0000100 -#define NOFLSH 0000200 -#define TOSTOP 0000400 -#define ECHOCTL 0001000 -#define ECHOPRT 0002000 -#define ECHOKE 0004000 -#define FLUSHO 0010000 -#define PENDIN 0040000 -#define IEXTEN 0100000 - - -/* tcflow() and TCXONC use these */ -#define TCOOFF 0 -#define TCOON 1 -#define TCIOFF 2 -#define TCION 3 - -/* tcflush() and TCFLSH use these */ -#define TCIFLUSH 0 -#define TCOFLUSH 1 -#define TCIOFLUSH 2 - -/* tcsetattr uses these */ -#define TCSANOW 0 -#define TCSADRAIN 1 -#define TCSAFLUSH 2 - -#endif /* __V850_TERMBITS_H__ */ diff --git a/include/asm-v850/termios.h b/include/asm-v850/termios.h deleted file mode 100644 index fcd171838d9c..000000000000 --- a/include/asm-v850/termios.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef __V850_TERMIOS_H__ -#define __V850_TERMIOS_H__ - -#include -#include - -struct winsize { - unsigned short ws_row; - unsigned short ws_col; - unsigned short ws_xpixel; - unsigned short ws_ypixel; -}; - -#define NCC 8 -struct termio { - unsigned short c_iflag; /* input mode flags */ - unsigned short c_oflag; /* output mode flags */ - unsigned short c_cflag; /* control mode flags */ - unsigned short c_lflag; /* local mode flags */ - unsigned char c_line; /* line discipline */ - unsigned char c_cc[NCC]; /* control characters */ -}; - -/* modem lines */ -#define TIOCM_LE 0x001 -#define TIOCM_DTR 0x002 -#define TIOCM_RTS 0x004 -#define TIOCM_ST 0x008 -#define TIOCM_SR 0x010 -#define TIOCM_CTS 0x020 -#define TIOCM_CAR 0x040 -#define TIOCM_RNG 0x080 -#define TIOCM_DSR 0x100 -#define TIOCM_CD TIOCM_CAR -#define TIOCM_RI TIOCM_RNG -#define TIOCM_OUT1 0x2000 -#define TIOCM_OUT2 0x4000 -#define TIOCM_LOOP 0x8000 - -/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ - -#ifdef __KERNEL__ - -/* intr=^C quit=^\ erase=del kill=^U - eof=^D vtime=\0 vmin=\1 sxtc=\0 - start=^Q stop=^S susp=^Z eol=\0 - reprint=^R discard=^U werase=^W lnext=^V - eol2=\0 -*/ -#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" - -/* - * Translate a "termio" structure into a "termios". Ugh. - */ -#define SET_LOW_TERMIOS_BITS(termios, termio, x) { \ - unsigned short __tmp; \ - get_user(__tmp,&(termio)->x); \ - *(unsigned short *) &(termios)->x = __tmp; \ -} - -#define user_termio_to_kernel_termios(termios, termio) \ -({ \ - SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); \ - SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); \ - SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); \ - SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); \ - copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \ -}) - -/* - * Translate a "termios" structure into a "termio". Ugh. - */ -#define kernel_termios_to_user_termio(termio, termios) \ -({ \ - put_user((termios)->c_iflag, &(termio)->c_iflag); \ - put_user((termios)->c_oflag, &(termio)->c_oflag); \ - put_user((termios)->c_cflag, &(termio)->c_cflag); \ - put_user((termios)->c_lflag, &(termio)->c_lflag); \ - put_user((termios)->c_line, &(termio)->c_line); \ - copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \ -}) - -#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios2)) -#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios2)) -#define user_termios_to_kernel_termios_1(k, u) copy_from_user(k, u, sizeof(struct termios)) -#define kernel_termios_to_user_termios_1(u, k) copy_to_user(u, k, sizeof(struct termios)) - -#endif /* __KERNEL__ */ - -#endif /* __V850_TERMIOS_H__ */ diff --git a/include/asm-v850/thread_info.h b/include/asm-v850/thread_info.h deleted file mode 100644 index 1a9e6ae0c5fd..000000000000 --- a/include/asm-v850/thread_info.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * include/asm-v850/thread_info.h -- v850 low-level thread information - * - * Copyright (C) 2002 NEC Corporation - * Copyright (C) 2002 Miles Bader - * Copyright (C) 2002 David Howells (dhowells@redhat.com) - * - Incorporating suggestions made by Linus Torvalds and Dave Miller - * - * 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 for more details. - * - * This file was derived from the PPC version, include/asm-ppc/thread_info.h - * which was adapted from the i386 version by Paul Mackerras - */ - -#ifndef __V850_THREAD_INFO_H__ -#define __V850_THREAD_INFO_H__ - -#ifdef __KERNEL__ - -#ifndef __ASSEMBLY__ - -/* - * low level task data. - * If you change this, change the TI_* offsets below to match. - */ -struct thread_info { - struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ - unsigned long flags; /* low level flags */ - int cpu; /* cpu we're on */ - int preempt_count; /* 0 => preemptable, - <0 => BUG */ - struct restart_block restart_block; -}; - -#define INIT_THREAD_INFO(tsk) \ -{ \ - .task = &tsk, \ - .exec_domain = &default_exec_domain, \ - .flags = 0, \ - .cpu = 0, \ - .preempt_count = 1, \ - .restart_block = { \ - .fn = do_no_restart_syscall, \ - }, \ -} - -#define init_thread_info (init_thread_union.thread_info) -#define init_stack (init_thread_union.stack) - -/* - * macros/functions for gaining access to the thread information structure - */ - -/* thread information allocation */ -#define alloc_thread_info(tsk) ((struct thread_info *) \ - __get_free_pages(GFP_KERNEL, 1)) -#define free_thread_info(ti) free_pages((unsigned long) (ti), 1) - -#endif /* __ASSEMBLY__ */ - - -/* - * Offsets in thread_info structure, used in assembly code - */ -#define TI_TASK 0 -#define TI_EXECDOMAIN 4 -#define TI_FLAGS 8 -#define TI_CPU 12 -#define TI_PREEMPT 16 - -#define PREEMPT_ACTIVE 0x4000000 - -/* - * thread information flag bit numbers - */ -#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ -#define TIF_SIGPENDING 1 /* signal pending */ -#define TIF_NEED_RESCHED 2 /* rescheduling necessary */ -#define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling - TIF_NEED_RESCHED */ -#define TIF_MEMDIE 4 - -/* as above, but as bit values */ -#define _TIF_SYSCALL_TRACE (1< - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_TLB_H__ -#define __V850_TLB_H__ - -#define tlb_flush(tlb) ((void)0) - -#include - -#endif /* __V850_TLB_H__ */ diff --git a/include/asm-v850/tlbflush.h b/include/asm-v850/tlbflush.h deleted file mode 100644 index c44aa64449c8..000000000000 --- a/include/asm-v850/tlbflush.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * include/asm-v850/tlbflush.h - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_TLBFLUSH_H__ -#define __V850_TLBFLUSH_H__ - -#include - - -/* - * flush all user-space atc entries. - */ -static inline void __flush_tlb(void) -{ - BUG (); -} - -static inline void __flush_tlb_one(unsigned long addr) -{ - BUG (); -} - -#define flush_tlb() __flush_tlb() - -/* - * flush all atc entries (both kernel and user-space entries). - */ -static inline void flush_tlb_all(void) -{ - BUG (); -} - -static inline void flush_tlb_mm(struct mm_struct *mm) -{ - BUG (); -} - -static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) -{ - BUG (); -} - -static inline void flush_tlb_range(struct vm_area_struct *vma, - unsigned long start, unsigned long end) -{ - BUG (); -} - -static inline void flush_tlb_kernel_page(unsigned long addr) -{ - BUG (); -} - -#endif /* __V850_TLBFLUSH_H__ */ diff --git a/include/asm-v850/topology.h b/include/asm-v850/topology.h deleted file mode 100644 index 6040e41d7945..000000000000 --- a/include/asm-v850/topology.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __V850_TOPOLOGY_H__ -#define __V850_TOPOLOGY_H__ - -#include - -#endif /* __V850_TOPOLOGY_H__ */ diff --git a/include/asm-v850/types.h b/include/asm-v850/types.h deleted file mode 100644 index 89f735ee41dd..000000000000 --- a/include/asm-v850/types.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef __V850_TYPES_H__ -#define __V850_TYPES_H__ - -#ifndef __ASSEMBLY__ - -/* - * This file is never included by application software unless - * explicitly requested (e.g., via linux/types.h) in which case the - * application is Linux specific so (user-) name space pollution is - * not a major issue. However, for interoperability, libraries still - * need to be careful to avoid a name clashes. - */ -#include - -typedef unsigned short umode_t; - -#endif /* !__ASSEMBLY__ */ - -/* - * These aren't exported outside the kernel to avoid name space clashes - */ -#ifdef __KERNEL__ - -#define BITS_PER_LONG 32 - -#ifndef __ASSEMBLY__ - -/* Dma addresses are 32-bits wide. */ - -typedef u32 dma_addr_t; - -#endif /* !__ASSEMBLY__ */ - -#endif /* __KERNEL__ */ - -#endif /* __V850_TYPES_H__ */ diff --git a/include/asm-v850/uaccess.h b/include/asm-v850/uaccess.h deleted file mode 100644 index 64563c409bb2..000000000000 --- a/include/asm-v850/uaccess.h +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef __V850_UACCESS_H__ -#define __V850_UACCESS_H__ - -/* - * User space memory access functions - */ - -#include -#include - -#include -#include - -#define VERIFY_READ 0 -#define VERIFY_WRITE 1 - -static inline int access_ok (int type, const void *addr, unsigned long size) -{ - /* XXX I guess we should check against real ram bounds at least, and - possibly make sure ADDR is not within the kernel. - For now we just check to make sure it's not a small positive - or negative value, as that will at least catch some kinds of - error. In particular, we make sure that ADDR's not within the - interrupt vector area, which we know starts at zero, or within the - peripheral-I/O area, which is located just _before_ zero. */ - unsigned long val = (unsigned long)addr; - return val >= (0x80 + NUM_CPU_IRQS*16) && val < 0xFFFFF000; -} - -/* - * The exception table consists of pairs of addresses: the first is the - * address of an instruction that is allowed to fault, and the second is - * the address at which the program should continue. No registers are - * modified, so it is entirely up to the continuation code to figure out - * what to do. - * - * All the routines below use bits of fixup code that are out of line - * with the main instruction path. This means when everything is well, - * we don't even have to jump over them. Further, they do not intrude - * on our cache or tlb entries. - */ - -struct exception_table_entry -{ - unsigned long insn, fixup; -}; - -/* Returns 0 if exception not found and fixup otherwise. */ -extern unsigned long search_exception_table (unsigned long); - - -/* - * These are the main single-value transfer routines. They automatically - * use the right size if we just have the right pointer type. - */ - -extern int bad_user_access_length (void); - -#define __get_user(var, ptr) \ - ({ \ - int __gu_err = 0; \ - typeof(*(ptr)) __gu_val = 0; \ - switch (sizeof (*(ptr))) { \ - case 1: \ - case 2: \ - case 4: \ - __gu_val = *(ptr); \ - break; \ - case 8: \ - memcpy(&__gu_val, ptr, sizeof(__gu_val)); \ - break; \ - default: \ - __gu_val = 0; \ - __gu_err = __get_user_bad (); \ - break; \ - } \ - (var) = __gu_val; \ - __gu_err; \ - }) -#define __get_user_bad() (bad_user_access_length (), (-EFAULT)) - -#define __put_user(var, ptr) \ - ({ \ - int __pu_err = 0; \ - switch (sizeof (*(ptr))) { \ - case 1: \ - case 2: \ - case 4: \ - *(ptr) = (var); \ - break; \ - case 8: { \ - typeof(*(ptr)) __pu_val = 0; \ - memcpy(ptr, &__pu_val, sizeof(__pu_val)); \ - } \ - break; \ - default: \ - __pu_err = __put_user_bad (); \ - break; \ - } \ - __pu_err; \ - }) -#define __put_user_bad() (bad_user_access_length (), (-EFAULT)) - -#define put_user(x, ptr) __put_user(x, ptr) -#define get_user(x, ptr) __get_user(x, ptr) - -#define __copy_from_user(to, from, n) (memcpy (to, from, n), 0) -#define __copy_to_user(to, from, n) (memcpy(to, from, n), 0) - -#define __copy_to_user_inatomic __copy_to_user -#define __copy_from_user_inatomic __copy_from_user - -#define copy_from_user(to, from, n) __copy_from_user (to, from, n) -#define copy_to_user(to, from, n) __copy_to_user(to, from, n) - -#define copy_to_user_ret(to,from,n,retval) \ - ({ if (copy_to_user (to,from,n)) return retval; }) - -#define copy_from_user_ret(to,from,n,retval) \ - ({ if (copy_from_user (to,from,n)) return retval; }) - -/* - * Copy a null terminated string from userspace. - */ - -static inline long -strncpy_from_user (char *dst, const char *src, long count) -{ - char *tmp; - strncpy (dst, src, count); - for (tmp = dst; *tmp && count > 0; tmp++, count--) - ; - return tmp - dst; -} - -/* - * Return the size of a string (including the ending 0) - * - * Return 0 on exception, a value greater than N if too long - */ -static inline long strnlen_user (const char *src, long n) -{ - return strlen (src) + 1; -} - -#define strlen_user(str) strnlen_user (str, 32767) - -/* - * Zero Userspace - */ - -static inline unsigned long -clear_user (void *to, unsigned long n) -{ - memset (to, 0, n); - return 0; -} - -#endif /* __V850_UACCESS_H__ */ diff --git a/include/asm-v850/ucontext.h b/include/asm-v850/ucontext.h deleted file mode 100644 index 303c21590cff..000000000000 --- a/include/asm-v850/ucontext.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __V850_UCONTEXT_H__ -#define __V850_UCONTEXT_H__ - -#include - -struct ucontext { - unsigned long uc_flags; - struct ucontext *uc_link; - stack_t uc_stack; - struct sigcontext uc_mcontext; - sigset_t uc_sigmask; /* mask last for extensibility */ -}; - -#endif /* __V850_UCONTEXT_H__ */ diff --git a/include/asm-v850/unaligned.h b/include/asm-v850/unaligned.h deleted file mode 100644 index 53122b28491e..000000000000 --- a/include/asm-v850/unaligned.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2001 NEC Corporation - * Copyright (C) 2001 Miles Bader - * - * 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 for more details. - * - * Note that some v850 chips support unaligned access, but it seems too - * annoying to use. - */ -#ifndef _ASM_V850_UNALIGNED_H -#define _ASM_V850_UNALIGNED_H - -#include -#include -#include - -#define get_unaligned __get_unaligned_le -#define put_unaligned __put_unaligned_le - -#endif /* _ASM_V850_UNALIGNED_H */ diff --git a/include/asm-v850/unistd.h b/include/asm-v850/unistd.h deleted file mode 100644 index 2241ed45ecfe..000000000000 --- a/include/asm-v850/unistd.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - * include/asm-v850/unistd.h -- System call numbers and invocation mechanism - * - * Copyright (C) 2001,02,03,04 NEC Electronics Corporation - * Copyright (C) 2001,02,03,04 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_UNISTD_H__ -#define __V850_UNISTD_H__ - -#define __NR_restart_syscall 0 -#define __NR_exit 1 -#define __NR_fork 2 -#define __NR_read 3 -#define __NR_write 4 -#define __NR_open 5 -#define __NR_close 6 -#define __NR_waitpid 7 -#define __NR_creat 8 -#define __NR_link 9 -#define __NR_unlink 10 -#define __NR_execve 11 -#define __NR_chdir 12 -#define __NR_time 13 -#define __NR_mknod 14 -#define __NR_chmod 15 -#define __NR_chown 16 -#define __NR_break 17 -#define __NR_lseek 19 -#define __NR_getpid 20 -#define __NR_mount 21 -#define __NR_umount 22 -#define __NR_setuid 23 -#define __NR_getuid 24 -#define __NR_stime 25 -#define __NR_ptrace 26 -#define __NR_alarm 27 -#define __NR_pause 29 -#define __NR_utime 30 -#define __NR_stty 31 -#define __NR_gtty 32 -#define __NR_access 33 -#define __NR_nice 34 -#define __NR_ftime 35 -#define __NR_sync 36 -#define __NR_kill 37 -#define __NR_rename 38 -#define __NR_mkdir 39 -#define __NR_rmdir 40 -#define __NR_dup 41 -#define __NR_pipe 42 -#define __NR_times 43 -#define __NR_prof 44 -#define __NR_brk 45 -#define __NR_setgid 46 -#define __NR_getgid 47 -#define __NR_signal 48 -#define __NR_geteuid 49 -#define __NR_getegid 50 -#define __NR_acct 51 -#define __NR_umount2 52 -#define __NR_lock 53 -#define __NR_ioctl 54 -#define __NR_fcntl 55 -#define __NR_setpgid 57 -#define __NR_umask 60 -#define __NR_chroot 61 -#define __NR_ustat 62 -#define __NR_dup2 63 -#define __NR_getppid 64 -#define __NR_getpgrp 65 -#define __NR_setsid 66 -#define __NR_sigaction 67 -#define __NR_sgetmask 68 -#define __NR_ssetmask 69 -#define __NR_setreuid 70 -#define __NR_setregid 71 -#define __NR_sigsuspend 72 -#define __NR_sigpending 73 -#define __NR_sethostname 74 -#define __NR_setrlimit 75 -#define __NR_ugetrlimit 76 -#define __NR_getrusage 77 -#define __NR_gettimeofday 78 -#define __NR_settimeofday 79 -#define __NR_getgroups 80 -#define __NR_setgroups 81 -#define __NR_select 82 -#define __NR_symlink 83 -#define __NR_readlink 85 -#define __NR_uselib 86 -#define __NR_swapon 87 -#define __NR_reboot 88 -#define __NR_readdir 89 -#define __NR_mmap 90 -#define __NR_munmap 91 -#define __NR_truncate 92 -#define __NR_ftruncate 93 -#define __NR_fchmod 94 -#define __NR_fchown 95 -#define __NR_getpriority 96 -#define __NR_setpriority 97 -#define __NR_profil 98 -#define __NR_statfs 99 -#define __NR_fstatfs 100 -#define __NR_socketcall 102 -#define __NR_syslog 103 -#define __NR_setitimer 104 -#define __NR_getitimer 105 -#define __NR_stat 106 -#define __NR_lstat 107 -#define __NR_fstat 108 -#define __NR_vhangup 111 -#define __NR_wait4 114 -#define __NR_swapoff 115 -#define __NR_sysinfo 116 -#define __NR_ipc 117 -#define __NR_fsync 118 -#define __NR_sigreturn 119 -#define __NR_clone 120 -#define __NR_setdomainname 121 -#define __NR_uname 122 -#define __NR_cacheflush 123 -#define __NR_adjtimex 124 -#define __NR_mprotect 125 -#define __NR_sigprocmask 126 -#define __NR_create_module 127 -#define __NR_init_module 128 -#define __NR_delete_module 129 -#define __NR_get_kernel_syms 130 -#define __NR_quotactl 131 -#define __NR_getpgid 132 -#define __NR_fchdir 133 -#define __NR_bdflush 134 -#define __NR_sysfs 135 -#define __NR_personality 136 -#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define __NR_setfsuid 138 -#define __NR_setfsgid 139 -#define __NR__llseek 140 -#define __NR_getdents 141 -#define __NR_flock 143 -#define __NR_msync 144 -#define __NR_readv 145 -#define __NR_writev 146 -#define __NR_getsid 147 -#define __NR_fdatasync 148 -#define __NR__sysctl 149 -#define __NR_mlock 150 -#define __NR_munlock 151 -#define __NR_mlockall 152 -#define __NR_munlockall 153 -#define __NR_sched_setparam 154 -#define __NR_sched_getparam 155 -#define __NR_sched_setscheduler 156 -#define __NR_sched_getscheduler 157 -#define __NR_sched_yield 158 -#define __NR_sched_get_priority_max 159 -#define __NR_sched_get_priority_min 160 -#define __NR_sched_rr_get_interval 161 -#define __NR_nanosleep 162 -#define __NR_mremap 163 -#define __NR_setresuid 164 -#define __NR_getresuid 165 -#define __NR_query_module 167 -#define __NR_poll 168 -#define __NR_nfsservctl 169 -#define __NR_setresgid 170 -#define __NR_getresgid 171 -#define __NR_prctl 172 -#define __NR_rt_sigreturn 173 -#define __NR_rt_sigaction 174 -#define __NR_rt_sigprocmask 175 -#define __NR_rt_sigpending 176 -#define __NR_rt_sigtimedwait 177 -#define __NR_rt_sigqueueinfo 178 -#define __NR_rt_sigsuspend 179 -#define __NR_pread 180 -#define __NR_pwrite 181 -#define __NR_lchown 182 -#define __NR_getcwd 183 -#define __NR_capget 184 -#define __NR_capset 185 -#define __NR_sigaltstack 186 -#define __NR_sendfile 187 -#define __NR_getpmsg 188 /* some people actually want streams */ -#define __NR_putpmsg 189 /* some people actually want streams */ -#define __NR_vfork 190 -#define __NR_mmap2 192 -#define __NR_truncate64 193 -#define __NR_ftruncate64 194 -#define __NR_stat64 195 -#define __NR_lstat64 196 -#define __NR_fstat64 197 -#define __NR_fcntl64 198 -#define __NR_getdents64 199 -#define __NR_pivot_root 200 -#define __NR_gettid 201 -#define __NR_tkill 202 - -#ifdef __KERNEL__ - -#define __ARCH_WANT_IPC_PARSE_VERSION -#define __ARCH_WANT_OLD_READDIR -#define __ARCH_WANT_STAT64 -#define __ARCH_WANT_SYS_ALARM -#define __ARCH_WANT_SYS_GETHOSTNAME -#define __ARCH_WANT_SYS_PAUSE -#define __ARCH_WANT_SYS_SGETMASK -#define __ARCH_WANT_SYS_SIGNAL -#define __ARCH_WANT_SYS_TIME -#define __ARCH_WANT_SYS_UTIME -#define __ARCH_WANT_SYS_WAITPID -#define __ARCH_WANT_SYS_SOCKETCALL -#define __ARCH_WANT_SYS_FADVISE64 -#define __ARCH_WANT_SYS_GETPGRP -#define __ARCH_WANT_SYS_LLSEEK -#define __ARCH_WANT_SYS_NICE -#define __ARCH_WANT_SYS_OLDUMOUNT -#define __ARCH_WANT_SYS_SIGPENDING -#define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION - -/* - * "Conditional" syscalls - */ -#define cond_syscall(name) \ - asm (".weak\t" C_SYMBOL_STRING(name) ";" \ - ".set\t" C_SYMBOL_STRING(name) "," C_SYMBOL_STRING(sys_ni_syscall)) -#if 0 -/* This doesn't work if there's a function prototype for NAME visible, - because the argument types probably won't match. */ -#define cond_syscall(name) \ - void name (void) __attribute__ ((weak, alias ("sys_ni_syscall"))); -#endif - -#endif /* __KERNEL__ */ -#endif /* __V850_UNISTD_H__ */ diff --git a/include/asm-v850/user.h b/include/asm-v850/user.h deleted file mode 100644 index 63cdc567d272..000000000000 --- a/include/asm-v850/user.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef __V850_USER_H__ -#define __V850_USER_H__ - -/* Adapted from . */ - -#include -#include - -/* - * Core file format: The core file is written in such a way that gdb - * can understand it and provide useful information to the user (under - * linux we use the `trad-core' bfd, NOT the osf-core). The file contents - * are as follows: - * - * upage: 1 page consisting of a user struct that tells gdb - * what is present in the file. Directly after this is a - * copy of the task_struct, which is currently not used by gdb, - * but it may come in handy at some point. All of the registers - * are stored as part of the upage. The upage should always be - * only one page long. - * data: The data segment follows next. We use current->end_text to - * current->brk to pick up all of the user variables, plus any memory - * that may have been sbrk'ed. No attempt is made to determine if a - * page is demand-zero or if a page is totally unused, we just cover - * the entire range. All of the addresses are rounded in such a way - * that an integral number of pages is written. - * stack: We need the stack information in order to get a meaningful - * backtrace. We need to write the data from usp to - * current->start_stack, so we round each of these in order to be able - * to write an integer number of pages. - */ -struct user { - struct pt_regs regs; /* entire machine state */ - size_t u_tsize; /* text size (pages) */ - size_t u_dsize; /* data size (pages) */ - size_t u_ssize; /* stack size (pages) */ - unsigned long start_code; /* text starting address */ - unsigned long start_data; /* data starting address */ - unsigned long start_stack; /* stack starting address */ - long int signal; /* signal causing core dump */ - unsigned long u_ar0; /* help gdb find registers */ - unsigned long magic; /* identifies a core file */ - char u_comm[32]; /* user command name */ -}; - -#define NBPG PAGE_SIZE -#define UPAGES 1 -#define HOST_TEXT_START_ADDR (u.start_code) -#define HOST_DATA_START_ADDR (u.start_data) -#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) - -#endif /* __V850_USER_H__ */ diff --git a/include/asm-v850/v850e.h b/include/asm-v850/v850e.h deleted file mode 100644 index 5a222eb5117f..000000000000 --- a/include/asm-v850/v850e.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * include/asm-v850/v850e.h -- V850E CPU - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E_H__ -#define __V850_V850E_H__ - -#include - -#define CPU_ARCH "v850e" - -#endif /* __V850_V850E_H__ */ diff --git a/include/asm-v850/v850e2.h b/include/asm-v850/v850e2.h deleted file mode 100644 index 48680408ab7e..000000000000 --- a/include/asm-v850/v850e2.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * include/asm-v850/v850e2.h -- Machine-dependent defs for V850E2 CPUs - * - * Copyright (C) 2002,03 NEC Electronics Corporation - * Copyright (C) 2002,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E2_H__ -#define __V850_V850E2_H__ - -#include /* v850e-style interrupt system. */ - - -#define CPU_ARCH "v850e2" - - -/* Control registers. */ - -/* Chip area select control */ -#define V850E2_CSC_ADDR(n) (0xFFFFF060 + (n) * 2) -#define V850E2_CSC(n) (*(volatile u16 *)V850E2_CSC_ADDR(n)) -/* I/O area select control */ -#define V850E2_BPC_ADDR 0xFFFFF064 -#define V850E2_BPC (*(volatile u16 *)V850E2_BPC_ADDR) -/* Bus size configuration */ -#define V850E2_BSC_ADDR 0xFFFFF066 -#define V850E2_BSC (*(volatile u16 *)V850E2_BSC_ADDR) -/* Endian configuration */ -#define V850E2_BEC_ADDR 0xFFFFF068 -#define V850E2_BEC (*(volatile u16 *)V850E2_BEC_ADDR) -/* Cache configuration */ -#define V850E2_BHC_ADDR 0xFFFFF06A -#define V850E2_BHC (*(volatile u16 *)V850E2_BHC_ADDR) -/* NPB strobe-wait configuration */ -#define V850E2_VSWC_ADDR 0xFFFFF06E -#define V850E2_VSWC (*(volatile u16 *)V850E2_VSWC_ADDR) -/* Bus cycle type */ -#define V850E2_BCT_ADDR(n) (0xFFFFF480 + (n) * 2) -#define V850E2_BCT(n) (*(volatile u16 *)V850E2_BCT_ADDR(n)) -/* Data wait control */ -#define V850E2_DWC_ADDR(n) (0xFFFFF484 + (n) * 2) -#define V850E2_DWC(n) (*(volatile u16 *)V850E2_DWC_ADDR(n)) -/* Bus cycle control */ -#define V850E2_BCC_ADDR 0xFFFFF488 -#define V850E2_BCC (*(volatile u16 *)V850E2_BCC_ADDR) -/* Address wait control */ -#define V850E2_ASC_ADDR 0xFFFFF48A -#define V850E2_ASC (*(volatile u16 *)V850E2_ASC_ADDR) -/* Local bus sizing control */ -#define V850E2_LBS_ADDR 0xFFFFF48E -#define V850E2_LBS (*(volatile u16 *)V850E2_LBS_ADDR) -/* Line buffer control */ -#define V850E2_LBC_ADDR(n) (0xFFFFF490 + (n) * 2) -#define V850E2_LBC(n) (*(volatile u16 *)V850E2_LBC_ADDR(n)) -/* SDRAM configuration */ -#define V850E2_SCR_ADDR(n) (0xFFFFF4A0 + (n) * 4) -#define V850E2_SCR(n) (*(volatile u16 *)V850E2_SCR_ADDR(n)) -/* SDRAM refresh cycle control */ -#define V850E2_RFS_ADDR(n) (0xFFFFF4A2 + (n) * 4) -#define V850E2_RFS(n) (*(volatile u16 *)V850E2_RFS_ADDR(n)) - - -#endif /* __V850_V850E2_H__ */ diff --git a/include/asm-v850/v850e2_cache.h b/include/asm-v850/v850e2_cache.h deleted file mode 100644 index 87edf0d311d5..000000000000 --- a/include/asm-v850/v850e2_cache.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * include/asm-v850/v850e2_cache_cache.h -- Cache control for V850E2 - * cache memories - * - * Copyright (C) 2003,05 NEC Electronics Corporation - * Copyright (C) 2003,05 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E2_CACHE_H__ -#define __V850_V850E2_CACHE_H__ - -#include - - -/* Cache control registers. */ - -/* Bus Transaction Control */ -#define V850E2_CACHE_BTSC_ADDR 0xFFFFF070 -#define V850E2_CACHE_BTSC (*(volatile u16 *)V850E2_CACHE_BTSC_ADDR) -#define V850E2_CACHE_BTSC_ICM 0x0001 /* icache enable */ -#define V850E2_CACHE_BTSC_DCM0 0x0004 /* dcache enable, bit 0 */ -#define V850E2_CACHE_BTSC_DCM1 0x0008 /* dcache enable, bit 1 */ -#define V850E2_CACHE_BTSC_DCM_WT /* write-through */ \ - V850E2_CACHE_BTSC_DCM0 -#ifdef CONFIG_V850E2_V850E2S -# define V850E2_CACHE_BTSC_DCM_WB_NO_ALLOC /* write-back, non-alloc */ \ - V850E2_CACHE_BTSC_DCM1 -# define V850E2_CACHE_BTSC_DCM_WB_ALLOC /* write-back, non-alloc */ \ - (V850E2_CACHE_BTSC_DCM1 | V850E2_CACHE_BTSC_DCM0) -# define V850E2_CACHE_BTSC_ISEQ 0x0010 /* icache `address sequence mode' */ -# define V850E2_CACHE_BTSC_DSEQ 0x0020 /* dcache `address sequence mode' */ -# define V850E2_CACHE_BTSC_IRFC 0x0030 -# define V850E2_CACHE_BTSC_ILCD 0x4000 -# define V850E2_CACHE_BTSC_VABE 0x8000 -#endif /* CONFIG_V850E2_V850E2S */ - -/* Cache operation start address register (low-bits). */ -#define V850E2_CACHE_CADL_ADDR 0xFFFFF074 -#define V850E2_CACHE_CADL (*(volatile u16 *)V850E2_CACHE_CADL_ADDR) -/* Cache operation start address register (high-bits). */ -#define V850E2_CACHE_CADH_ADDR 0xFFFFF076 -#define V850E2_CACHE_CADH (*(volatile u16 *)V850E2_CACHE_CADH_ADDR) -/* Cache operation count register. */ -#define V850E2_CACHE_CCNT_ADDR 0xFFFFF078 -#define V850E2_CACHE_CCNT (*(volatile u16 *)V850E2_CACHE_CCNT_ADDR) -/* Cache operation specification register. */ -#define V850E2_CACHE_COPR_ADDR 0xFFFFF07A -#define V850E2_CACHE_COPR (*(volatile u16 *)V850E2_CACHE_COPR_ADDR) -#define V850E2_CACHE_COPR_STRT 0x0001 /* start cache operation */ -#define V850E2_CACHE_COPR_LBSL 0x0100 /* 0 = icache, 1 = dcache */ -#define V850E2_CACHE_COPR_WSLE 0x0200 /* operate on cache way */ -#define V850E2_CACHE_COPR_WSL(way) ((way) * 0x0400) /* way select */ -#define V850E2_CACHE_COPR_CFC(op) ((op) * 0x1000) /* cache function code */ - - -/* Size of a cache line in bytes. */ -#define V850E2_CACHE_LINE_SIZE_BITS 4 -#define V850E2_CACHE_LINE_SIZE (1 << V850E2_CACHE_LINE_SIZE_BITS) - -/* The size of each cache `way' in lines. */ -#define V850E2_CACHE_WAY_SIZE 256 - - -/* For */ -#define L1_CACHE_BYTES V850E2_CACHE_LINE_SIZE -#define L1_CACHE_SHIFT V850E2_CACHE_LINE_SIZE_BITS - - -#endif /* __V850_V850E2_CACHE_H__ */ diff --git a/include/asm-v850/v850e_cache.h b/include/asm-v850/v850e_cache.h deleted file mode 100644 index aa7d7eb9da50..000000000000 --- a/include/asm-v850/v850e_cache.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * include/asm-v850/v850e_cache.h -- Cache control for V850E cache memories - * - * Copyright (C) 2001,03 NEC Electronics Corporation - * Copyright (C) 2001,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -/* This file implements cache control for the rather simple cache used on - some V850E CPUs, specifically the NB85E/TEG CPU-core and the V850E/ME2 - CPU. V850E2 processors have their own (better) cache - implementation. */ - -#ifndef __V850_V850E_CACHE_H__ -#define __V850_V850E_CACHE_H__ - -#include - - -/* Cache control registers. */ -#define V850E_CACHE_BHC_ADDR 0xFFFFF06A -#define V850E_CACHE_BHC (*(volatile u16 *)V850E_CACHE_BHC_ADDR) -#define V850E_CACHE_ICC_ADDR 0xFFFFF070 -#define V850E_CACHE_ICC (*(volatile u16 *)V850E_CACHE_ICC_ADDR) -#define V850E_CACHE_ISI_ADDR 0xFFFFF072 -#define V850E_CACHE_ISI (*(volatile u16 *)V850E_CACHE_ISI_ADDR) -#define V850E_CACHE_DCC_ADDR 0xFFFFF078 -#define V850E_CACHE_DCC (*(volatile u16 *)V850E_CACHE_DCC_ADDR) - -/* Size of a cache line in bytes. */ -#define V850E_CACHE_LINE_SIZE 16 - -/* For */ -#define L1_CACHE_BYTES V850E_CACHE_LINE_SIZE - - -#if defined(__KERNEL__) && !defined(__ASSEMBLY__) -/* Set caching params via the BHC, ICC, and DCC registers. */ -void v850e_cache_enable (u16 bhc, u16 icc, u16 dcc); -#endif /* __KERNEL__ && !__ASSEMBLY__ */ - - -#endif /* __V850_V850E_CACHE_H__ */ diff --git a/include/asm-v850/v850e_intc.h b/include/asm-v850/v850e_intc.h deleted file mode 100644 index 6fdf95708317..000000000000 --- a/include/asm-v850/v850e_intc.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * include/asm-v850/v850e_intc.h -- V850E CPU interrupt controller (INTC) - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E_INTC_H__ -#define __V850_V850E_INTC_H__ - - -/* There are 4 16-bit `Interrupt Mask Registers' located contiguously - starting from this base. Each interrupt uses a single bit to - indicated enabled/disabled status. */ -#define V850E_INTC_IMR_BASE_ADDR 0xFFFFF100 -#define V850E_INTC_IMR_ADDR(irq) (V850E_INTC_IMR_BASE_ADDR + ((irq) >> 3)) -#define V850E_INTC_IMR_BIT(irq) ((irq) & 0x7) - -/* Each maskable interrupt has a single-byte control register at this - address. */ -#define V850E_INTC_IC_BASE_ADDR 0xFFFFF110 -#define V850E_INTC_IC_ADDR(irq) (V850E_INTC_IC_BASE_ADDR + ((irq) << 1)) -#define V850E_INTC_IC(irq) (*(volatile u8 *)V850E_INTC_IC_ADDR(irq)) -/* Encode priority PR for storing in an interrupt control register. */ -#define V850E_INTC_IC_PR(pr) (pr) -/* Interrupt disable bit in an interrupt control register. */ -#define V850E_INTC_IC_MK_BIT 6 -#define V850E_INTC_IC_MK (1 << V850E_INTC_IC_MK_BIT) -/* Interrupt pending flag in an interrupt control register. */ -#define V850E_INTC_IC_IF_BIT 7 -#define V850E_INTC_IC_IF (1 << V850E_INTC_IC_IF_BIT) - -/* The ISPR (In-service priority register) contains one bit for each interrupt - priority level, which is set to one when that level is currently being - serviced (and thus blocking any interrupts of equal or lesser level). */ -#define V850E_INTC_ISPR_ADDR 0xFFFFF1FA -#define V850E_INTC_ISPR (*(volatile u8 *)V850E_INTC_ISPR_ADDR) - - -#ifndef __ASSEMBLY__ - -/* Enable interrupt handling for interrupt IRQ. */ -static inline void v850e_intc_enable_irq (unsigned irq) -{ - __asm__ __volatile__ ("clr1 %0, [%1]" - :: "r" (V850E_INTC_IMR_BIT (irq)), - "r" (V850E_INTC_IMR_ADDR (irq)) - : "memory"); -} - -/* Disable interrupt handling for interrupt IRQ. Note that any - interrupts received while disabled will be delivered once the - interrupt is enabled again, unless they are explicitly cleared using - `v850e_intc_clear_pending_irq'. */ -static inline void v850e_intc_disable_irq (unsigned irq) -{ - __asm__ __volatile__ ("set1 %0, [%1]" - :: "r" (V850E_INTC_IMR_BIT (irq)), - "r" (V850E_INTC_IMR_ADDR (irq)) - : "memory"); -} - -/* Return true if interrupt handling for interrupt IRQ is enabled. */ -static inline int v850e_intc_irq_enabled (unsigned irq) -{ - int rval; - __asm__ __volatile__ ("tst1 %1, [%2]; setf z, %0" - : "=r" (rval) - : "r" (V850E_INTC_IMR_BIT (irq)), - "r" (V850E_INTC_IMR_ADDR (irq))); - return rval; -} - -/* Disable irqs from 0 until LIMIT. LIMIT must be a multiple of 8. */ -static inline void _v850e_intc_disable_irqs (unsigned limit) -{ - unsigned long addr; - for (addr = V850E_INTC_IMR_BASE_ADDR; limit >= 8; addr++, limit -= 8) - *(char *)addr = 0xFF; -} - -/* Disable all irqs. This is purposely a macro, because NUM_MACH_IRQS - will be only be defined later. */ -#define v850e_intc_disable_irqs() _v850e_intc_disable_irqs (NUM_MACH_IRQS) - -/* Clear any pending interrupts for IRQ. */ -static inline void v850e_intc_clear_pending_irq (unsigned irq) -{ - __asm__ __volatile__ ("clr1 %0, 0[%1]" - :: "i" (V850E_INTC_IC_IF_BIT), - "r" (V850E_INTC_IC_ADDR (irq)) - : "memory"); -} - -/* Return true if interrupt IRQ is pending (but disabled). */ -static inline int v850e_intc_irq_pending (unsigned irq) -{ - int rval; - __asm__ __volatile__ ("tst1 %1, 0[%2]; setf nz, %0" - : "=r" (rval) - : "i" (V850E_INTC_IC_IF_BIT), - "r" (V850E_INTC_IC_ADDR (irq))); - return rval; -} - - -struct v850e_intc_irq_init { - const char *name; /* name of interrupt type */ - - /* Range of kernel irq numbers for this type: - BASE, BASE+INTERVAL, ..., BASE+INTERVAL*NUM */ - unsigned base, num, interval; - - unsigned priority; /* interrupt priority to assign */ -}; -struct hw_interrupt_type; /* fwd decl */ - -/* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array - INITS (which is terminated by an entry with the name field == 0). */ -extern void v850e_intc_init_irq_types (struct v850e_intc_irq_init *inits, - struct hw_interrupt_type *hw_irq_types); - - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_V850E_INTC_H__ */ diff --git a/include/asm-v850/v850e_timer_c.h b/include/asm-v850/v850e_timer_c.h deleted file mode 100644 index f70575df6ea9..000000000000 --- a/include/asm-v850/v850e_timer_c.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * include/asm-v850/v850e_timer_c.h -- `Timer C' component often used - * with the V850E cpu core - * - * Copyright (C) 2001,03 NEC Electronics Corporation - * Copyright (C) 2001,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -/* NOTE: this include file currently contains only enough to allow us to - use timer C as an interrupt pass-through. */ - -#ifndef __V850_V850E_TIMER_C_H__ -#define __V850_V850E_TIMER_C_H__ - -#include -#include /* Pick up chip-specific defs. */ - - -/* Timer C (16-bit interval timers). */ - -/* Control register 0 for timer C. */ -#define V850E_TIMER_C_TMCC0_ADDR(n) (V850E_TIMER_C_BASE_ADDR + 0x6 + 0x10 *(n)) -#define V850E_TIMER_C_TMCC0(n) (*(volatile u8 *)V850E_TIMER_C_TMCC0_ADDR(n)) -#define V850E_TIMER_C_TMCC0_CAE 0x01 /* clock action enable */ -#define V850E_TIMER_C_TMCC0_CE 0x02 /* count enable */ -/* ... */ - -/* Control register 1 for timer C. */ -#define V850E_TIMER_C_TMCC1_ADDR(n) (V850E_TIMER_C_BASE_ADDR + 0x8 + 0x10 *(n)) -#define V850E_TIMER_C_TMCC1(n) (*(volatile u8 *)V850E_TIMER_C_TMCC1_ADDR(n)) -#define V850E_TIMER_C_TMCC1_CMS0 0x01 /* capture/compare mode select (ccc0) */ -#define V850E_TIMER_C_TMCC1_CMS1 0x02 /* capture/compare mode select (ccc1) */ -/* ... */ - -/* Interrupt edge-sensitivity control for timer C. */ -#define V850E_TIMER_C_SESC_ADDR(n) (V850E_TIMER_C_BASE_ADDR + 0x9 + 0x10 *(n)) -#define V850E_TIMER_C_SESC(n) (*(volatile u8 *)V850E_TIMER_C_SESC_ADDR(n)) - -/* ...etc... */ - - -#endif /* __V850_V850E_TIMER_C_H__ */ diff --git a/include/asm-v850/v850e_timer_d.h b/include/asm-v850/v850e_timer_d.h deleted file mode 100644 index 417612c5b22f..000000000000 --- a/include/asm-v850/v850e_timer_d.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * include/asm-v850/v850e_timer_d.h -- `Timer D' component often used - * with the V850E cpu core - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E_TIMER_D_H__ -#define __V850_V850E_TIMER_D_H__ - -#include -#include /* Pick up chip-specific defs. */ - - -/* Timer D (16-bit interval timers). */ - -/* Count registers for timer D. */ -#define V850E_TIMER_D_TMD_ADDR(n) (V850E_TIMER_D_TMD_BASE_ADDR + 0x10 * (n)) -#define V850E_TIMER_D_TMD(n) (*(volatile u16 *)V850E_TIMER_D_TMD_ADDR(n)) - -/* Count compare registers for timer D. */ -#define V850E_TIMER_D_CMD_ADDR(n) (V850E_TIMER_D_CMD_BASE_ADDR + 0x10 * (n)) -#define V850E_TIMER_D_CMD(n) (*(volatile u16 *)V850E_TIMER_D_CMD_ADDR(n)) - -/* Control registers for timer D. */ -#define V850E_TIMER_D_TMCD_ADDR(n) (V850E_TIMER_D_TMCD_BASE_ADDR + 0x10 * (n)) -#define V850E_TIMER_D_TMCD(n) (*(volatile u8 *)V850E_TIMER_D_TMCD_ADDR(n)) -/* Control bits for timer D. */ -#define V850E_TIMER_D_TMCD_CE 0x2 /* count enable */ -#define V850E_TIMER_D_TMCD_CAE 0x1 /* clock action enable */ -/* Clock divider setting (log2). */ -#define V850E_TIMER_D_TMCD_CS(divlog2) (((divlog2) - V850E_TIMER_D_TMCD_CS_MIN) << 4) -/* Minimum clock divider setting (log2). */ -#ifndef V850E_TIMER_D_TMCD_CS_MIN /* Can be overridden by mach-specific hdrs */ -#define V850E_TIMER_D_TMCD_CS_MIN 2 /* Default is correct for the v850e/ma1 */ -#endif -/* Maximum clock divider setting (log2). */ -#define V850E_TIMER_D_TMCD_CS_MAX (V850E_TIMER_D_TMCD_CS_MIN + 7) - -/* Return the clock-divider (log2) of timer D unit N. */ -#define V850E_TIMER_D_DIVLOG2(n) \ - (((V850E_TIMER_D_TMCD(n) >> 4) & 0x7) + V850E_TIMER_D_TMCD_CS_MIN) - - -#ifndef __ASSEMBLY__ - -/* Start interval timer TIMER (0-3). The timer will issue the - corresponding INTCMD interrupt RATE times per second. This function - does not enable the interrupt. */ -extern void v850e_timer_d_configure (unsigned timer, unsigned rate); - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_V850E_TIMER_D_H__ */ diff --git a/include/asm-v850/v850e_uart.h b/include/asm-v850/v850e_uart.h deleted file mode 100644 index 5182fb4cc989..000000000000 --- a/include/asm-v850/v850e_uart.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * include/asm-v850/v850e_uart.h -- common V850E on-chip UART driver - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -/* There's not actually a single UART implementation used by V850E CPUs, - but rather a series of implementations that are all `close' to one - another. This file corresponds to the single driver which handles all - of them. */ - -#ifndef __V850_V850E_UART_H__ -#define __V850_V850E_UART_H__ - -#include - -#include -#include -#include /* Pick up chip-specific defs. */ - - -/* Include model-specific definitions. */ -#ifdef CONFIG_V850E_UART -# ifdef CONFIG_V850E_UARTB -# include -# else -# include /* original V850E UART */ -# endif -#endif - - -/* Optional capabilities some hardware provides. */ - -/* This UART doesn't implement RTS/CTS by default, but some platforms - implement them externally, so check to see if defined - anything. */ -#ifdef V850E_UART_CTS -#define v850e_uart_cts(n) V850E_UART_CTS(n) -#else -#define v850e_uart_cts(n) (1) -#endif - -/* Do the same for RTS. */ -#ifdef V850E_UART_SET_RTS -#define v850e_uart_set_rts(n,v) V850E_UART_SET_RTS(n,v) -#else -#define v850e_uart_set_rts(n,v) ((void)0) -#endif - - -/* This is the serial channel to use for the boot console (if desired). */ -#ifndef V850E_UART_CONSOLE_CHANNEL -# define V850E_UART_CONSOLE_CHANNEL 0 -#endif - - -#ifndef __ASSEMBLY__ - -/* Setup a console using channel 0 of the builtin uart. */ -extern void v850e_uart_cons_init (unsigned chan); - -/* Configure and turn on uart channel CHAN, using the termios `control - modes' bits in CFLAGS, and a baud-rate of BAUD. */ -void v850e_uart_configure (unsigned chan, unsigned cflags, unsigned baud); - -#endif /* !__ASSEMBLY__ */ - - -#endif /* __V850_V850E_UART_H__ */ diff --git a/include/asm-v850/v850e_uarta.h b/include/asm-v850/v850e_uarta.h deleted file mode 100644 index e483e0950725..000000000000 --- a/include/asm-v850/v850e_uarta.h +++ /dev/null @@ -1,278 +0,0 @@ -/* - * include/asm-v850/v850e_uarta.h -- original V850E on-chip UART - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -/* This is the original V850E UART implementation is called just `UART' in - the docs, but we name this header file because the - name is used for the common driver that handles both - `UART' and `UARTB' implementations. */ - -#ifndef __V850_V850E_UARTA_H__ -#define __V850_V850E_UARTA_H__ - - -/* Raw hardware interface. */ - -/* The base address of the UART control registers for channel N. - The default is the address used on the V850E/MA1. */ -#ifndef V850E_UART_BASE_ADDR -#define V850E_UART_BASE_ADDR(n) (0xFFFFFA00 + 0x10 * (n)) -#endif - -/* Addresses of specific UART control registers for channel N. - The defaults are the addresses used on the V850E/MA1; if a platform - wants to redefine any of these, it must redefine them all. */ -#ifndef V850E_UART_ASIM_ADDR -#define V850E_UART_ASIM_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x0) -#define V850E_UART_RXB_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x2) -#define V850E_UART_ASIS_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x3) -#define V850E_UART_TXB_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x4) -#define V850E_UART_ASIF_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x5) -#define V850E_UART_CKSR_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x6) -#define V850E_UART_BRGC_ADDR(n) (V850E_UART_BASE_ADDR(n) + 0x7) -#endif - -/* UART config registers. */ -#define V850E_UART_ASIM(n) (*(volatile u8 *)V850E_UART_ASIM_ADDR(n)) -/* Control bits for config registers. */ -#define V850E_UART_ASIM_CAE 0x80 /* clock enable */ -#define V850E_UART_ASIM_TXE 0x40 /* transmit enable */ -#define V850E_UART_ASIM_RXE 0x20 /* receive enable */ -#define V850E_UART_ASIM_PS_MASK 0x18 /* mask covering parity-select bits */ -#define V850E_UART_ASIM_PS_NONE 0x00 /* no parity */ -#define V850E_UART_ASIM_PS_ZERO 0x08 /* zero parity */ -#define V850E_UART_ASIM_PS_ODD 0x10 /* odd parity */ -#define V850E_UART_ASIM_PS_EVEN 0x18 /* even parity */ -#define V850E_UART_ASIM_CL_8 0x04 /* char len is 8 bits (otherwise, 7) */ -#define V850E_UART_ASIM_SL_2 0x02 /* 2 stop bits (otherwise, 1) */ -#define V850E_UART_ASIM_ISRM 0x01 /* generate INTSR interrupt on errors - (otherwise, generate INTSER) */ - -/* UART serial interface status registers. */ -#define V850E_UART_ASIS(n) (*(volatile u8 *)V850E_UART_ASIS_ADDR(n)) -/* Control bits for status registers. */ -#define V850E_UART_ASIS_PE 0x04 /* parity error */ -#define V850E_UART_ASIS_FE 0x02 /* framing error */ -#define V850E_UART_ASIS_OVE 0x01 /* overrun error */ - -/* UART serial interface transmission status registers. */ -#define V850E_UART_ASIF(n) (*(volatile u8 *)V850E_UART_ASIF_ADDR(n)) -#define V850E_UART_ASIF_TXBF 0x02 /* transmit buffer flag (data in TXB) */ -#define V850E_UART_ASIF_TXSF 0x01 /* transmit shift flag (sending data) */ - -/* UART receive buffer register. */ -#define V850E_UART_RXB(n) (*(volatile u8 *)V850E_UART_RXB_ADDR(n)) - -/* UART transmit buffer register. */ -#define V850E_UART_TXB(n) (*(volatile u8 *)V850E_UART_TXB_ADDR(n)) - -/* UART baud-rate generator control registers. */ -#define V850E_UART_CKSR(n) (*(volatile u8 *)V850E_UART_CKSR_ADDR(n)) -#define V850E_UART_CKSR_MAX 11 -#define V850E_UART_BRGC(n) (*(volatile u8 *)V850E_UART_BRGC_ADDR(n)) -#define V850E_UART_BRGC_MIN 8 - - -#ifndef V850E_UART_CKSR_MAX_FREQ -#define V850E_UART_CKSR_MAX_FREQ (25*1000*1000) -#endif - -/* Calculate the minimum value for CKSR on this processor. */ -static inline unsigned v850e_uart_cksr_min (void) -{ - int min = 0; - unsigned freq = V850E_UART_BASE_FREQ; - while (freq > V850E_UART_CKSR_MAX_FREQ) { - freq >>= 1; - min++; - } - return min; -} - - -/* Slightly abstract interface used by driver. */ - - -/* Interrupts used by the UART. */ - -/* Received when the most recently transmitted character has been sent. */ -#define V850E_UART_TX_IRQ(chan) IRQ_INTST (chan) -/* Received when a new character has been received. */ -#define V850E_UART_RX_IRQ(chan) IRQ_INTSR (chan) - - -/* UART clock generator interface. */ - -/* This type encapsulates a particular uart frequency. */ -typedef struct { - unsigned clk_divlog2; - unsigned brgen_count; -} v850e_uart_speed_t; - -/* Calculate a uart speed from BAUD for this uart. */ -static inline v850e_uart_speed_t v850e_uart_calc_speed (unsigned baud) -{ - v850e_uart_speed_t speed; - - /* Calculate the log2 clock divider and baud-rate counter values - (note that the UART divides the resulting clock by 2, so - multiply BAUD by 2 here to compensate). */ - calc_counter_params (V850E_UART_BASE_FREQ, baud * 2, - v850e_uart_cksr_min(), - V850E_UART_CKSR_MAX, 8/*bits*/, - &speed.clk_divlog2, &speed.brgen_count); - - return speed; -} - -/* Return the current speed of uart channel CHAN. */ -static inline v850e_uart_speed_t v850e_uart_speed (unsigned chan) -{ - v850e_uart_speed_t speed; - speed.clk_divlog2 = V850E_UART_CKSR (chan); - speed.brgen_count = V850E_UART_BRGC (chan); - return speed; -} - -/* Set the current speed of uart channel CHAN. */ -static inline void v850e_uart_set_speed(unsigned chan,v850e_uart_speed_t speed) -{ - V850E_UART_CKSR (chan) = speed.clk_divlog2; - V850E_UART_BRGC (chan) = speed.brgen_count; -} - -static inline int -v850e_uart_speed_eq (v850e_uart_speed_t speed1, v850e_uart_speed_t speed2) -{ - return speed1.clk_divlog2 == speed2.clk_divlog2 - && speed1.brgen_count == speed2.brgen_count; -} - -/* Minimum baud rate possible. */ -#define v850e_uart_min_baud() \ - ((V850E_UART_BASE_FREQ >> V850E_UART_CKSR_MAX) / (2 * 255) + 1) - -/* Maximum baud rate possible. The error is quite high at max, though. */ -#define v850e_uart_max_baud() \ - ((V850E_UART_BASE_FREQ >> v850e_uart_cksr_min()) / (2 *V850E_UART_BRGC_MIN)) - -/* The `maximum' clock rate the uart can used, which is wanted (though not - really used in any useful way) by the serial framework. */ -#define v850e_uart_max_clock() \ - ((V850E_UART_BASE_FREQ >> v850e_uart_cksr_min()) / 2) - - -/* UART configuration interface. */ - -/* Type of the uart config register; must be a scalar. */ -typedef u16 v850e_uart_config_t; - -/* The uart hardware config register for channel CHAN. */ -#define V850E_UART_CONFIG(chan) V850E_UART_ASIM (chan) - -/* This config bit set if the uart is enabled. */ -#define V850E_UART_CONFIG_ENABLED V850E_UART_ASIM_CAE -/* If the uart _isn't_ enabled, store this value to it to do so. */ -#define V850E_UART_CONFIG_INIT V850E_UART_ASIM_CAE -/* Store this config value to disable the uart channel completely. */ -#define V850E_UART_CONFIG_FINI 0 - -/* Setting/clearing these bits enable/disable TX/RX, respectively (but - otherwise generally leave things running). */ -#define V850E_UART_CONFIG_RX_ENABLE V850E_UART_ASIM_RXE -#define V850E_UART_CONFIG_TX_ENABLE V850E_UART_ASIM_TXE - -/* These masks define which config bits affect TX/RX modes, respectively. */ -#define V850E_UART_CONFIG_RX_BITS \ - (V850E_UART_ASIM_PS_MASK | V850E_UART_ASIM_CL_8 | V850E_UART_ASIM_ISRM) -#define V850E_UART_CONFIG_TX_BITS \ - (V850E_UART_ASIM_PS_MASK | V850E_UART_ASIM_CL_8 | V850E_UART_ASIM_SL_2) - -static inline v850e_uart_config_t v850e_uart_calc_config (unsigned cflags) -{ - v850e_uart_config_t config = 0; - - /* Figure out new configuration of control register. */ - if (cflags & CSTOPB) - /* Number of stop bits, 1 or 2. */ - config |= V850E_UART_ASIM_SL_2; - if ((cflags & CSIZE) == CS8) - /* Number of data bits, 7 or 8. */ - config |= V850E_UART_ASIM_CL_8; - if (! (cflags & PARENB)) - /* No parity check/generation. */ - config |= V850E_UART_ASIM_PS_NONE; - else if (cflags & PARODD) - /* Odd parity check/generation. */ - config |= V850E_UART_ASIM_PS_ODD; - else - /* Even parity check/generation. */ - config |= V850E_UART_ASIM_PS_EVEN; - if (cflags & CREAD) - /* Reading enabled. */ - config |= V850E_UART_ASIM_RXE; - - config |= V850E_UART_ASIM_CAE; - config |= V850E_UART_ASIM_TXE; /* Writing is always enabled. */ - config |= V850E_UART_ASIM_ISRM; /* Errors generate a read-irq. */ - - return config; -} - -/* This should delay as long as necessary for a recently written config - setting to settle, before we turn the uart back on. */ -static inline void -v850e_uart_config_delay (v850e_uart_config_t config, v850e_uart_speed_t speed) -{ - /* The UART may not be reset properly unless we wait at least 2 - `basic-clocks' until turning on the TXE/RXE bits again. - A `basic clock' is the clock used by the baud-rate generator, - i.e., the cpu clock divided by the 2^new_clk_divlog2. - The loop takes 2 insns, so loop CYCLES / 2 times. */ - register unsigned count = 1 << speed.clk_divlog2; - while (--count != 0) - /* nothing */; -} - - -/* RX/TX interface. */ - -/* Return true if all characters awaiting transmission on uart channel N - have been transmitted. */ -#define v850e_uart_xmit_done(n) \ - (! (V850E_UART_ASIF(n) & V850E_UART_ASIF_TXBF)) -/* Wait for this to be true. */ -#define v850e_uart_wait_for_xmit_done(n) \ - do { } while (! v850e_uart_xmit_done (n)) - -/* Return true if uart channel N is ready to transmit a character. */ -#define v850e_uart_xmit_ok(n) \ - (v850e_uart_xmit_done(n) && v850e_uart_cts(n)) -/* Wait for this to be true. */ -#define v850e_uart_wait_for_xmit_ok(n) \ - do { } while (! v850e_uart_xmit_ok (n)) - -/* Write character CH to uart channel CHAN. */ -#define v850e_uart_putc(chan, ch) (V850E_UART_TXB(chan) = (ch)) - -/* Return latest character read on channel CHAN. */ -#define v850e_uart_getc(chan) V850E_UART_RXB (chan) - -/* Return bit-mask of uart error status. */ -#define v850e_uart_err(chan) V850E_UART_ASIS (chan) -/* Various error bits set in the error result. */ -#define V850E_UART_ERR_OVERRUN V850E_UART_ASIS_OVE -#define V850E_UART_ERR_FRAME V850E_UART_ASIS_FE -#define V850E_UART_ERR_PARITY V850E_UART_ASIS_PE - - -#endif /* __V850_V850E_UARTA_H__ */ diff --git a/include/asm-v850/v850e_uartb.h b/include/asm-v850/v850e_uartb.h deleted file mode 100644 index 6d4767d5a835..000000000000 --- a/include/asm-v850/v850e_uartb.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * include/asm-v850/v850e_uartb.h -- V850E on-chip `UARTB' UART - * - * Copyright (C) 2001,02,03 NEC Electronics Corporation - * Copyright (C) 2001,02,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -/* The V850E UARTB is basically a superset of the original V850E UART, but - even where it's the same, the names and details have changed a bit. - It's similar enough to use the same driver (v850e_uart.c), but the - details have been abstracted slightly to do so. */ - -#ifndef __V850_V850E_UARTB_H__ -#define __V850_V850E_UARTB_H__ - - -/* Raw hardware interface. */ - -#define V850E_UARTB_BASE_ADDR(n) (0xFFFFFA00 + 0x10 * (n)) - -/* Addresses of specific UART control registers for channel N. */ -#define V850E_UARTB_CTL0_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x0) -#define V850E_UARTB_CTL2_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x2) -#define V850E_UARTB_STR_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x4) -#define V850E_UARTB_RX_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x6) -#define V850E_UARTB_RXAP_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x6) -#define V850E_UARTB_TX_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0x8) -#define V850E_UARTB_FIC0_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0xA) -#define V850E_UARTB_FIC1_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0xB) -#define V850E_UARTB_FIC2_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0xC) -#define V850E_UARTB_FIS0_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0xE) -#define V850E_UARTB_FIS1_ADDR(n) (V850E_UARTB_BASE_ADDR(n) + 0xF) - -/* UARTB control register 0 (general config). */ -#define V850E_UARTB_CTL0(n) (*(volatile u8 *)V850E_UARTB_CTL0_ADDR(n)) -/* Control bits for config registers. */ -#define V850E_UARTB_CTL0_PWR 0x80 /* clock enable */ -#define V850E_UARTB_CTL0_TXE 0x40 /* transmit enable */ -#define V850E_UARTB_CTL0_RXE 0x20 /* receive enable */ -#define V850E_UARTB_CTL0_DIR 0x10 /* */ -#define V850E_UARTB_CTL0_PS1 0x08 /* parity */ -#define V850E_UARTB_CTL0_PS0 0x04 /* parity */ -#define V850E_UARTB_CTL0_CL 0x02 /* char len 1:8bit, 0:7bit */ -#define V850E_UARTB_CTL0_SL 0x01 /* stop bit 1:2bit, 0:1bit */ -#define V850E_UARTB_CTL0_PS_MASK 0x0C /* mask covering parity bits */ -#define V850E_UARTB_CTL0_PS_NONE 0x00 /* no parity */ -#define V850E_UARTB_CTL0_PS_ZERO 0x04 /* zero parity */ -#define V850E_UARTB_CTL0_PS_ODD 0x08 /* odd parity */ -#define V850E_UARTB_CTL0_PS_EVEN 0x0C /* even parity */ -#define V850E_UARTB_CTL0_CL_8 0x02 /* char len 1:8bit, 0:7bit */ -#define V850E_UARTB_CTL0_SL_2 0x01 /* stop bit 1:2bit, 0:1bit */ - -/* UARTB control register 2 (clock divider). */ -#define V850E_UARTB_CTL2(n) (*(volatile u16 *)V850E_UARTB_CTL2_ADDR(n)) -#define V850E_UARTB_CTL2_MIN 4 -#define V850E_UARTB_CTL2_MAX 0xFFFF - -/* UARTB serial interface status register. */ -#define V850E_UARTB_STR(n) (*(volatile u8 *)V850E_UARTB_STR_ADDR(n)) -/* Control bits for status registers. */ -#define V850E_UARTB_STR_TSF 0x80 /* UBTX or FIFO exist data */ -#define V850E_UARTB_STR_OVF 0x08 /* overflow error */ -#define V850E_UARTB_STR_PE 0x04 /* parity error */ -#define V850E_UARTB_STR_FE 0x02 /* framing error */ -#define V850E_UARTB_STR_OVE 0x01 /* overrun error */ - -/* UARTB receive data register. */ -#define V850E_UARTB_RX(n) (*(volatile u8 *)V850E_UARTB_RX_ADDR(n)) -#define V850E_UARTB_RXAP(n) (*(volatile u16 *)V850E_UARTB_RXAP_ADDR(n)) -/* Control bits for status registers. */ -#define V850E_UARTB_RXAP_PEF 0x0200 /* parity error */ -#define V850E_UARTB_RXAP_FEF 0x0100 /* framing error */ - -/* UARTB transmit data register. */ -#define V850E_UARTB_TX(n) (*(volatile u8 *)V850E_UARTB_TX_ADDR(n)) - -/* UARTB FIFO control register 0. */ -#define V850E_UARTB_FIC0(n) (*(volatile u8 *)V850E_UARTB_FIC0_ADDR(n)) - -/* UARTB FIFO control register 1. */ -#define V850E_UARTB_FIC1(n) (*(volatile u8 *)V850E_UARTB_FIC1_ADDR(n)) - -/* UARTB FIFO control register 2. */ -#define V850E_UARTB_FIC2(n) (*(volatile u16 *)V850E_UARTB_FIC2_ADDR(n)) - -/* UARTB FIFO status register 0. */ -#define V850E_UARTB_FIS0(n) (*(volatile u8 *)V850E_UARTB_FIS0_ADDR(n)) - -/* UARTB FIFO status register 1. */ -#define V850E_UARTB_FIS1(n) (*(volatile u8 *)V850E_UARTB_FIS1_ADDR(n)) - - -/* Slightly abstract interface used by driver. */ - - -/* Interrupts used by the UART. */ - -/* Received when the most recently transmitted character has been sent. */ -#define V850E_UART_TX_IRQ(chan) IRQ_INTUBTIT (chan) -/* Received when a new character has been received. */ -#define V850E_UART_RX_IRQ(chan) IRQ_INTUBTIR (chan) - -/* Use by serial driver for information purposes. */ -#define V850E_UART_BASE_ADDR(chan) V850E_UARTB_BASE_ADDR(chan) - - -/* UART clock generator interface. */ - -/* This type encapsulates a particular uart frequency. */ -typedef u16 v850e_uart_speed_t; - -/* Calculate a uart speed from BAUD for this uart. */ -static inline v850e_uart_speed_t v850e_uart_calc_speed (unsigned baud) -{ - v850e_uart_speed_t speed; - - /* - * V850E/ME2 UARTB baud rate is determined by the value of UBCTL2 - * fx = V850E_UARTB_BASE_FREQ = CPU_CLOCK_FREQ/4 - * baud = fx / 2*speed [ speed >= 4 ] - */ - speed = V850E_UARTB_CTL2_MIN; - while (((V850E_UARTB_BASE_FREQ / 2) / speed ) > baud) - speed++; - - return speed; -} - -/* Return the current speed of uart channel CHAN. */ -#define v850e_uart_speed(chan) V850E_UARTB_CTL2 (chan) - -/* Set the current speed of uart channel CHAN. */ -#define v850e_uart_set_speed(chan, speed) (V850E_UARTB_CTL2 (chan) = (speed)) - -/* Return true if SPEED1 and SPEED2 are the same. */ -#define v850e_uart_speed_eq(speed1, speed2) ((speed1) == (speed2)) - -/* Minimum baud rate possible. */ -#define v850e_uart_min_baud() \ - ((V850E_UARTB_BASE_FREQ / 2) / V850E_UARTB_CTL2_MAX) - -/* Maximum baud rate possible. The error is quite high at max, though. */ -#define v850e_uart_max_baud() \ - ((V850E_UARTB_BASE_FREQ / 2) / V850E_UARTB_CTL2_MIN) - -/* The `maximum' clock rate the uart can used, which is wanted (though not - really used in any useful way) by the serial framework. */ -#define v850e_uart_max_clock() \ - (V850E_UARTB_BASE_FREQ / 2) - - -/* UART configuration interface. */ - -/* Type of the uart config register; must be a scalar. */ -typedef u16 v850e_uart_config_t; - -/* The uart hardware config register for channel CHAN. */ -#define V850E_UART_CONFIG(chan) V850E_UARTB_CTL0 (chan) - -/* This config bit set if the uart is enabled. */ -#define V850E_UART_CONFIG_ENABLED V850E_UARTB_CTL0_PWR -/* If the uart _isn't_ enabled, store this value to it to do so. */ -#define V850E_UART_CONFIG_INIT V850E_UARTB_CTL0_PWR -/* Store this config value to disable the uart channel completely. */ -#define V850E_UART_CONFIG_FINI 0 - -/* Setting/clearing these bits enable/disable TX/RX, respectively (but - otherwise generally leave things running). */ -#define V850E_UART_CONFIG_RX_ENABLE V850E_UARTB_CTL0_RXE -#define V850E_UART_CONFIG_TX_ENABLE V850E_UARTB_CTL0_TXE - -/* These masks define which config bits affect TX/RX modes, respectively. */ -#define V850E_UART_CONFIG_RX_BITS \ - (V850E_UARTB_CTL0_PS_MASK | V850E_UARTB_CTL0_CL_8) -#define V850E_UART_CONFIG_TX_BITS \ - (V850E_UARTB_CTL0_PS_MASK | V850E_UARTB_CTL0_CL_8 | V850E_UARTB_CTL0_SL_2) - -static inline v850e_uart_config_t v850e_uart_calc_config (unsigned cflags) -{ - v850e_uart_config_t config = 0; - - /* Figure out new configuration of control register. */ - if (cflags & CSTOPB) - /* Number of stop bits, 1 or 2. */ - config |= V850E_UARTB_CTL0_SL_2; - if ((cflags & CSIZE) == CS8) - /* Number of data bits, 7 or 8. */ - config |= V850E_UARTB_CTL0_CL_8; - if (! (cflags & PARENB)) - /* No parity check/generation. */ - config |= V850E_UARTB_CTL0_PS_NONE; - else if (cflags & PARODD) - /* Odd parity check/generation. */ - config |= V850E_UARTB_CTL0_PS_ODD; - else - /* Even parity check/generation. */ - config |= V850E_UARTB_CTL0_PS_EVEN; - if (cflags & CREAD) - /* Reading enabled. */ - config |= V850E_UARTB_CTL0_RXE; - - config |= V850E_UARTB_CTL0_PWR; - config |= V850E_UARTB_CTL0_TXE; /* Writing is always enabled. */ - config |= V850E_UARTB_CTL0_DIR; /* LSB first. */ - - return config; -} - -/* This should delay as long as necessary for a recently written config - setting to settle, before we turn the uart back on. */ -static inline void -v850e_uart_config_delay (v850e_uart_config_t config, v850e_uart_speed_t speed) -{ - /* The UART may not be reset properly unless we wait at least 2 - `basic-clocks' until turning on the TXE/RXE bits again. - A `basic clock' is the clock used by the baud-rate generator, - i.e., the cpu clock divided by the 2^new_clk_divlog2. - The loop takes 2 insns, so loop CYCLES / 2 times. */ - register unsigned count = 1 << speed; - while (--count != 0) - /* nothing */; -} - - -/* RX/TX interface. */ - -/* Return true if all characters awaiting transmission on uart channel N - have been transmitted. */ -#define v850e_uart_xmit_done(n) \ - (! (V850E_UARTB_STR(n) & V850E_UARTB_STR_TSF)) -/* Wait for this to be true. */ -#define v850e_uart_wait_for_xmit_done(n) \ - do { } while (! v850e_uart_xmit_done (n)) - -/* Return true if uart channel N is ready to transmit a character. */ -#define v850e_uart_xmit_ok(n) \ - (v850e_uart_xmit_done(n) && v850e_uart_cts(n)) -/* Wait for this to be true. */ -#define v850e_uart_wait_for_xmit_ok(n) \ - do { } while (! v850e_uart_xmit_ok (n)) - -/* Write character CH to uart channel CHAN. */ -#define v850e_uart_putc(chan, ch) (V850E_UARTB_TX(chan) = (ch)) - -/* Return latest character read on channel CHAN. */ -#define v850e_uart_getc(chan) V850E_UARTB_RX (chan) - -/* Return bit-mask of uart error status. */ -#define v850e_uart_err(chan) V850E_UARTB_STR (chan) -/* Various error bits set in the error result. */ -#define V850E_UART_ERR_OVERRUN V850E_UARTB_STR_OVE -#define V850E_UART_ERR_FRAME V850E_UARTB_STR_FE -#define V850E_UART_ERR_PARITY V850E_UARTB_STR_PE - - -#endif /* __V850_V850E_UARTB_H__ */ diff --git a/include/asm-v850/v850e_utils.h b/include/asm-v850/v850e_utils.h deleted file mode 100644 index 52eb72822d3d..000000000000 --- a/include/asm-v850/v850e_utils.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * include/asm-v850/v850e_utils.h -- Utility functions associated with - * V850E CPUs - * - * Copyright (C) 2001,03 NEC Electronics Corporation - * Copyright (C) 2001,03 Miles Bader - * - * 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 for more details. - * - * Written by Miles Bader - */ - -#ifndef __V850_V850E_UTILS_H__ -#define __V850_V850E_UTILS_H__ - -/* Calculate counter clock-divider and count values to attain the - desired frequency RATE from the base frequency BASE_FREQ. The - counter is expected to have a clock-divider, which can divide the - system cpu clock by a power of two value from MIN_DIVLOG2 to - MAX_DIV_LOG2, and a word-size of COUNTER_SIZE bits (the counter - counts up and resets whenever it's equal to the compare register, - generating an interrupt or whatever when it does so). The returned - values are: *DIVLOG2 -- log2 of the desired clock divider and *COUNT - -- the counter compare value to use. Returns true if it was possible - to find a reasonable value, otherwise false (and the other return - values will be set to be as good as possible). */ -extern int calc_counter_params (unsigned long base_freq, - unsigned long rate, - unsigned min_divlog2, unsigned max_divlog2, - unsigned counter_size, - unsigned *divlog2, unsigned *count); - -#endif /* __V850_V850E_UTILS_H__ */ diff --git a/include/linux/audit.h b/include/linux/audit.h index 8b82974bdc12..6272a395d43c 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -286,7 +286,6 @@ #define AUDIT_ARCH_SHEL64 (EM_SH|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define AUDIT_ARCH_SPARC (EM_SPARC) #define AUDIT_ARCH_SPARC64 (EM_SPARCV9|__AUDIT_ARCH_64BIT) -#define AUDIT_ARCH_V850 (EM_V850|__AUDIT_ARCH_LE) #define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define AUDIT_PERM_EXEC 1 diff --git a/include/linux/module.h b/include/linux/module.h index fce15ebd0e1c..68e09557c951 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -23,7 +23,7 @@ /* Not Yet Implemented */ #define MODULE_SUPPORTED_DEVICE(name) -/* v850 toolchain uses a `_' prefix for all user symbols */ +/* some toolchains uses a `_' prefix for all user symbols */ #ifndef MODULE_SYMBOL_PREFIX #define MODULE_SYMBOL_PREFIX "" #endif diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index f3a1c0e45021..3b2f6c04855e 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -59,9 +59,6 @@ #define PORT_SUNZILOG 38 #define PORT_SUNSAB 39 -/* NEC v850. */ -#define PORT_V850E_UART 40 - /* DEC */ #define PORT_DZ 46 #define PORT_ZS 47 diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 0522f368f9d7..4394dadff813 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -443,7 +443,7 @@ asmlinkage long sys_newuname(struct new_utsname __user *name); asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit __user *rlim); -#if defined(COMPAT_RLIM_OLD_INFINITY) || !(defined(CONFIG_IA64) || defined(CONFIG_V850)) +#if defined(COMPAT_RLIM_OLD_INFINITY) || !(defined(CONFIG_IA64)) asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit __user *rlim); #endif asmlinkage long sys_setrlimit(unsigned int resource, diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c index dca5e0dd09bf..4f8a3007e457 100644 --- a/scripts/genksyms/genksyms.c +++ b/scripts/genksyms/genksyms.c @@ -520,8 +520,7 @@ int main(int argc, char **argv) genksyms_usage(); return 1; } - if ((strcmp(arch, "v850") == 0) || (strcmp(arch, "h8300") == 0) - || (strcmp(arch, "blackfin") == 0)) + if ((strcmp(arch, "h8300") == 0) || (strcmp(arch, "blackfin") == 0)) mod_prefix = "_"; { extern int yydebug; diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 1fcaf3284a6a..4fa1f3ad2513 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -623,7 +623,7 @@ static int do_i2c_entry(const char *filename, struct i2c_device_id *id, return 1; } -/* Ignore any prefix, eg. v850 prepends _ */ +/* Ignore any prefix, eg. some architectures prepend _ */ static inline int sym_is(const char *symbol, const char *name) { const char *match; diff --git a/scripts/mod/mk_elfconfig.c b/scripts/mod/mk_elfconfig.c index db3881f14c2d..6a96d47bd1e6 100644 --- a/scripts/mod/mk_elfconfig.c +++ b/scripts/mod/mk_elfconfig.c @@ -55,7 +55,7 @@ main(int argc, char **argv) else exit(1); - if ((strcmp(argv[1], "v850") == 0) || (strcmp(argv[1], "h8300") == 0) + if ((strcmp(argv[1], "h8300") == 0) || (strcmp(argv[1], "blackfin") == 0)) printf("#define MODULE_SYMBOL_PREFIX \"_\"\n"); else -- cgit From a677a039be7243357d93502bff2b40850c942e2d Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:17 -0700 Subject: flag parameters: socket and socketpair This patch adds support for flag values which are ORed to the type passwd to socket and socketpair. The additional code is minimal. The flag values in this implementation can and must match the O_* flags. This avoids overhead in the conversion. The internal functions sock_alloc_fd and sock_map_fd get a new parameters and all callers are changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #define PORT 57392 /* For Linux these must be the same. */ #define SOCK_CLOEXEC O_CLOEXEC int main (void) { int fd; fd = socket (PF_INET, SOCK_STREAM, 0); if (fd == -1) { puts ("socket(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("socket(0) set close-on-exec flag"); return 1; } close (fd); fd = socket (PF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0); if (fd == -1) { puts ("socket(SOCK_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("socket(SOCK_CLOEXEC) does not set close-on-exec flag"); return 1; } close (fd); int fds[2]; if (socketpair (PF_UNIX, SOCK_STREAM, 0, fds) == -1) { puts ("socketpair(0) failed"); return 1; } for (int i = 0; i < 2; ++i) { coe = fcntl (fds[i], F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { printf ("socketpair(0) set close-on-exec flag for fds[%d]\n", i); return 1; } close (fds[i]); } if (socketpair (PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds) == -1) { puts ("socketpair(SOCK_CLOEXEC) failed"); return 1; } for (int i = 0; i < 2; ++i) { coe = fcntl (fds[i], F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { printf ("socketpair(SOCK_CLOEXEC) does not set close-on-exec flag for fds[%d]\n", i); return 1; } close (fds[i]); } puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: "David S. Miller" Cc: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-mips/socket.h | 7 +++++++ include/linux/net.h | 9 ++++++++- net/9p/trans_fd.c | 2 +- net/sctp/socket.c | 2 +- net/socket.c | 28 ++++++++++++++++++++-------- 5 files changed, 37 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/asm-mips/socket.h b/include/asm-mips/socket.h index 63f60254d308..facc2d7a87ca 100644 --- a/include/asm-mips/socket.h +++ b/include/asm-mips/socket.h @@ -102,6 +102,13 @@ enum sock_type { }; #define SOCK_MAX (SOCK_PACKET + 1) +/* Mask which covers at least up to SOCK_MASK-1. The + * * remaining bits are used as flags. */ +#define SOCK_TYPE_MASK 0xf + +/* Flags for socket, socketpair, paccept */ +#define SOCK_CLOEXEC O_CLOEXEC +#define SOCK_NONBLOCK O_NONBLOCK #define ARCH_HAS_SOCKET_TYPES 1 diff --git a/include/linux/net.h b/include/linux/net.h index 150a48c68d52..8b5383c45b45 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -20,6 +20,7 @@ #include #include +#include /* For O_CLOEXEC */ #include struct poll_table_struct; @@ -94,6 +95,12 @@ enum sock_type { }; #define SOCK_MAX (SOCK_PACKET + 1) +/* Mask which covers at least up to SOCK_MASK-1. The + * remaining bits are used as flags. */ +#define SOCK_TYPE_MASK 0xf + +/* Flags for socket, socketpair, paccept */ +#define SOCK_CLOEXEC O_CLOEXEC #endif /* ARCH_HAS_SOCKET_TYPES */ @@ -208,7 +215,7 @@ extern int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len); extern int sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags); -extern int sock_map_fd(struct socket *sock); +extern int sock_map_fd(struct socket *sock, int flags); extern struct socket *sockfd_lookup(int fd, int *err); #define sockfd_put(sock) fput(sock->file) extern int net_ratelimit(void); diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index 4507f744f44e..cdf137af7adc 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -1285,7 +1285,7 @@ static int p9_socket_open(struct p9_trans *trans, struct socket *csocket) int fd, ret; csocket->sk->sk_allocation = GFP_NOIO; - fd = sock_map_fd(csocket); + fd = sock_map_fd(csocket, 0); if (fd < 0) { P9_EPRINTK(KERN_ERR, "p9_socket_open: failed to map fd\n"); return fd; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 79bece16aede..dbb79adf8f3c 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3910,7 +3910,7 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval goto out; /* Map the socket to an unused fd that can be returned to the user. */ - retval = sock_map_fd(newsock); + retval = sock_map_fd(newsock, 0); if (retval < 0) { sock_release(newsock); goto out; diff --git a/net/socket.c b/net/socket.c index 1ba57d888981..64601f900352 100644 --- a/net/socket.c +++ b/net/socket.c @@ -349,11 +349,11 @@ static struct dentry_operations sockfs_dentry_operations = { * but we take care of internal coherence yet. */ -static int sock_alloc_fd(struct file **filep) +static int sock_alloc_fd(struct file **filep, int flags) { int fd; - fd = get_unused_fd(); + fd = get_unused_fd_flags(flags); if (likely(fd >= 0)) { struct file *file = get_empty_filp(); @@ -396,10 +396,10 @@ static int sock_attach_fd(struct socket *sock, struct file *file) return 0; } -int sock_map_fd(struct socket *sock) +int sock_map_fd(struct socket *sock, int flags) { struct file *newfile; - int fd = sock_alloc_fd(&newfile); + int fd = sock_alloc_fd(&newfile, flags); if (likely(fd >= 0)) { int err = sock_attach_fd(sock, newfile); @@ -1218,12 +1218,18 @@ asmlinkage long sys_socket(int family, int type, int protocol) { int retval; struct socket *sock; + int flags; + + flags = type & ~SOCK_TYPE_MASK; + if (flags & ~SOCK_CLOEXEC) + return -EINVAL; + type &= SOCK_TYPE_MASK; retval = sock_create(family, type, protocol, &sock); if (retval < 0) goto out; - retval = sock_map_fd(sock); + retval = sock_map_fd(sock, flags & O_CLOEXEC); if (retval < 0) goto out_release; @@ -1246,6 +1252,12 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, struct socket *sock1, *sock2; int fd1, fd2, err; struct file *newfile1, *newfile2; + int flags; + + flags = type & ~SOCK_TYPE_MASK; + if (flags & ~SOCK_CLOEXEC) + return -EINVAL; + type &= SOCK_TYPE_MASK; /* * Obtain the first socket and check if the underlying protocol @@ -1264,13 +1276,13 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, if (err < 0) goto out_release_both; - fd1 = sock_alloc_fd(&newfile1); + fd1 = sock_alloc_fd(&newfile1, flags & O_CLOEXEC); if (unlikely(fd1 < 0)) { err = fd1; goto out_release_both; } - fd2 = sock_alloc_fd(&newfile2); + fd2 = sock_alloc_fd(&newfile2, flags & O_CLOEXEC); if (unlikely(fd2 < 0)) { err = fd2; put_filp(newfile1); @@ -1426,7 +1438,7 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, */ __module_get(newsock->ops->owner); - newfd = sock_alloc_fd(&newfile); + newfd = sock_alloc_fd(&newfile, 0); if (unlikely(newfd < 0)) { err = newfd; sock_release(newsock); -- cgit From aaca0bdca573f3f51ea03139f9c7289541e7bca3 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:20 -0700 Subject: flag parameters: paccept This patch is by far the most complex in the series. It adds a new syscall paccept. This syscall differs from accept in that it adds (at the userlevel) two additional parameters: - a signal mask - a flags value The flags parameter can be used to set flag like SOCK_CLOEXEC. This is imlpemented here as well. Some people argued that this is a property which should be inherited from the file desriptor for the server but this is against POSIX. Additionally, we really want the signal mask parameter as well (similar to pselect, ppoll, etc). So an interface change in inevitable. The flag value is the same as for socket and socketpair. I think diverging here will only create confusion. Similar to the filesystem interfaces where the use of the O_* constants differs, it is acceptable here. The signal mask is handled as for pselect etc. The mask is temporarily installed for the thread and removed before the call returns. I modeled the code after pselect. If there is a problem it's likely also in pselect. For architectures which use socketcall I maintained this interface instead of adding a system call. The symmetry shouldn't be broken. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #include #include #include #include #ifndef __NR_paccept # ifdef __x86_64__ # define __NR_paccept 288 # elif defined __i386__ # define SYS_PACCEPT 18 # define USE_SOCKETCALL 1 # else # error "need __NR_paccept" # endif #endif #ifdef USE_SOCKETCALL # define paccept(fd, addr, addrlen, mask, flags) \ ({ long args[6] = { \ (long) fd, (long) addr, (long) addrlen, (long) mask, 8, (long) flags }; \ syscall (__NR_socketcall, SYS_PACCEPT, args); }) #else # define paccept(fd, addr, addrlen, mask, flags) \ syscall (__NR_paccept, fd, addr, addrlen, mask, 8, flags) #endif #define PORT 57392 #define SOCK_CLOEXEC O_CLOEXEC static pthread_barrier_t b; static void * tf (void *arg) { pthread_barrier_wait (&b); int s = socket (AF_INET, SOCK_STREAM, 0); struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); sin.sin_port = htons (PORT); connect (s, (const struct sockaddr *) &sin, sizeof (sin)); close (s); pthread_barrier_wait (&b); s = socket (AF_INET, SOCK_STREAM, 0); sin.sin_port = htons (PORT); connect (s, (const struct sockaddr *) &sin, sizeof (sin)); close (s); pthread_barrier_wait (&b); pthread_barrier_wait (&b); sleep (2); pthread_kill ((pthread_t) arg, SIGUSR1); return NULL; } static void handler (int s) { } int main (void) { pthread_barrier_init (&b, NULL, 2); struct sockaddr_in sin; pthread_t th; if (pthread_create (&th, NULL, tf, (void *) pthread_self ()) != 0) { puts ("pthread_create failed"); return 1; } int s = socket (AF_INET, SOCK_STREAM, 0); int reuse = 1; setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); sin.sin_port = htons (PORT); bind (s, (struct sockaddr *) &sin, sizeof (sin)); listen (s, SOMAXCONN); pthread_barrier_wait (&b); int s2 = paccept (s, NULL, 0, NULL, 0); if (s2 < 0) { puts ("paccept(0) failed"); return 1; } int coe = fcntl (s2, F_GETFD); if (coe & FD_CLOEXEC) { puts ("paccept(0) set close-on-exec-flag"); return 1; } close (s2); pthread_barrier_wait (&b); s2 = paccept (s, NULL, 0, NULL, SOCK_CLOEXEC); if (s2 < 0) { puts ("paccept(SOCK_CLOEXEC) failed"); return 1; } coe = fcntl (s2, F_GETFD); if ((coe & FD_CLOEXEC) == 0) { puts ("paccept(SOCK_CLOEXEC) does not set close-on-exec flag"); return 1; } close (s2); pthread_barrier_wait (&b); struct sigaction sa; sa.sa_handler = handler; sa.sa_flags = 0; sigemptyset (&sa.sa_mask); sigaction (SIGUSR1, &sa, NULL); sigset_t ss; pthread_sigmask (SIG_SETMASK, NULL, &ss); sigaddset (&ss, SIGUSR1); pthread_sigmask (SIG_SETMASK, &ss, NULL); sigdelset (&ss, SIGUSR1); alarm (4); pthread_barrier_wait (&b); errno = 0 ; s2 = paccept (s, NULL, 0, &ss, 0); if (s2 != -1 || errno != EINTR) { puts ("paccept did not fail with EINTR"); return 1; } close (s); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [akpm@linux-foundation.org: make it compile] [akpm@linux-foundation.org: add sys_ni stub] Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Cc: "David S. Miller" Cc: Roland McGrath Cc: Kyle McMartin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-alpha/socket.h | 5 +++ include/asm-parisc/socket.h | 5 +++ include/asm-x86/unistd_64.h | 2 ++ include/linux/net.h | 3 ++ include/linux/syscalls.h | 2 ++ kernel/sys_ni.c | 1 + net/compat.c | 52 ++++++++++++++++++++++++++--- net/socket.c | 81 ++++++++++++++++++++++++++++++++++++++++----- 8 files changed, 139 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/asm-alpha/socket.h b/include/asm-alpha/socket.h index 08c979319929..a1057c2d95e7 100644 --- a/include/asm-alpha/socket.h +++ b/include/asm-alpha/socket.h @@ -62,4 +62,9 @@ #define SO_MARK 36 +/* O_NONBLOCK clashes with the bits used for socket types. Therefore we + * have to define SOCK_NONBLOCK to a different value here. + */ +#define SOCK_NONBLOCK 0x40000000 + #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-parisc/socket.h b/include/asm-parisc/socket.h index 69a7a0d30b02..fba402c95ac2 100644 --- a/include/asm-parisc/socket.h +++ b/include/asm-parisc/socket.h @@ -54,4 +54,9 @@ #define SO_MARK 0x401f +/* O_NONBLOCK clashes with the bits used for socket types. Therefore we + * have to define SOCK_NONBLOCK to a different value here. + */ +#define SOCK_NONBLOCK 0x40000000 + #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index 9c1a4a3470d9..e323994a370f 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -639,6 +639,8 @@ __SYSCALL(__NR_fallocate, sys_fallocate) __SYSCALL(__NR_timerfd_settime, sys_timerfd_settime) #define __NR_timerfd_gettime 287 __SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime) +#define __NR_paccept 288 +__SYSCALL(__NR_paccept, sys_paccept) #ifndef __NO_STUBS diff --git a/include/linux/net.h b/include/linux/net.h index 8b5383c45b45..3a9b06d4d0fe 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -47,6 +47,7 @@ struct net; #define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */ #define SYS_SENDMSG 16 /* sys_sendmsg(2) */ #define SYS_RECVMSG 17 /* sys_recvmsg(2) */ +#define SYS_PACCEPT 18 /* sys_paccept(2) */ typedef enum { SS_FREE = 0, /* not allocated */ @@ -219,6 +220,8 @@ extern int sock_map_fd(struct socket *sock, int flags); extern struct socket *sockfd_lookup(int fd, int *err); #define sockfd_put(sock) fput(sock->file) extern int net_ratelimit(void); +extern long do_accept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen, int flags); #define net_random() random32() #define net_srandom(seed) srandom32((__force u32)seed) diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 4394dadff813..2a2a40af6b2c 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -409,6 +409,8 @@ asmlinkage long sys_getsockopt(int fd, int level, int optname, asmlinkage long sys_bind(int, struct sockaddr __user *, int); asmlinkage long sys_connect(int, struct sockaddr __user *, int); asmlinkage long sys_accept(int, struct sockaddr __user *, int __user *); +asmlinkage long sys_paccept(int, struct sockaddr __user *, int __user *, + const sigset_t *, size_t, int); asmlinkage long sys_getsockname(int, struct sockaddr __user *, int __user *); asmlinkage long sys_getpeername(int, struct sockaddr __user *, int __user *); asmlinkage long sys_send(int, void __user *, size_t, unsigned); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 0fea0ee12da9..2f0b8a2e600f 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -31,6 +31,7 @@ cond_syscall(sys_socketpair); cond_syscall(sys_bind); cond_syscall(sys_listen); cond_syscall(sys_accept); +cond_syscall(sys_paccept); cond_syscall(sys_connect); cond_syscall(sys_getsockname); cond_syscall(sys_getpeername); diff --git a/net/compat.c b/net/compat.c index 6e1b03b51933..67fb6a3834a3 100644 --- a/net/compat.c +++ b/net/compat.c @@ -722,9 +722,10 @@ EXPORT_SYMBOL(compat_mc_getsockopt); /* Argument list sizes for compat_sys_socketcall */ #define AL(x) ((x) * sizeof(u32)) -static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), +static unsigned char nas[19]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), - AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3), + AL(6)}; #undef AL asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags) @@ -737,13 +738,52 @@ 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_paccept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen, + const compat_sigset_t __user *sigmask, + compat_size_t sigsetsize, int flags) +{ + compat_sigset_t ss32; + sigset_t ksigmask, sigsaved; + int ret; + + if (sigmask) { + if (sigsetsize != sizeof(compat_sigset_t)) + return -EINVAL; + if (copy_from_user(&ss32, sigmask, sizeof(ss32))) + return -EFAULT; + sigset_from_compat(&ksigmask, &ss32); + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = do_accept(fd, upeer_sockaddr, upeer_addrlen, flags); + + if (ret == -ERESTARTNOHAND) { + /* + * Don't restore the signal mask yet. Let do_signal() deliver + * the signal on the way back to userspace, before the signal + * mask is restored. + */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, + sizeof(sigsaved)); + set_restore_sigmask(); + } + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + return ret; +} + asmlinkage long compat_sys_socketcall(int call, u32 __user *args) { int ret; u32 a[6]; u32 a0, a1; - if (call < SYS_SOCKET || call > SYS_RECVMSG) + if (call < SYS_SOCKET || call > SYS_PACCEPT) return -EINVAL; if (copy_from_user(a, args, nas[call])) return -EFAULT; @@ -764,7 +804,7 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args) ret = sys_listen(a0, a1); break; case SYS_ACCEPT: - ret = sys_accept(a0, compat_ptr(a1), compat_ptr(a[2])); + ret = do_accept(a0, compat_ptr(a1), compat_ptr(a[2]), 0); break; case SYS_GETSOCKNAME: ret = sys_getsockname(a0, compat_ptr(a1), compat_ptr(a[2])); @@ -804,6 +844,10 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args) case SYS_RECVMSG: ret = compat_sys_recvmsg(a0, compat_ptr(a1), a[2]); break; + case SYS_PACCEPT: + ret = compat_sys_paccept(a0, compat_ptr(a1), compat_ptr(a[2]), + compat_ptr(a[3]), a[4], a[5]); + break; default: ret = -EINVAL; break; diff --git a/net/socket.c b/net/socket.c index 64601f900352..a0ce8ad72252 100644 --- a/net/socket.c +++ b/net/socket.c @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -1225,6 +1226,9 @@ asmlinkage long sys_socket(int family, int type, int protocol) return -EINVAL; type &= SOCK_TYPE_MASK; + if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) + flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; + retval = sock_create(family, type, protocol, &sock); if (retval < 0) goto out; @@ -1259,6 +1263,9 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, return -EINVAL; type &= SOCK_TYPE_MASK; + if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) + flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; + /* * Obtain the first socket and check if the underlying protocol * supports the socketpair call. @@ -1413,14 +1420,20 @@ asmlinkage long sys_listen(int fd, int backlog) * clean when we restucture accept also. */ -asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, - int __user *upeer_addrlen) +long do_accept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen, int flags) { struct socket *sock, *newsock; struct file *newfile; int err, len, newfd, fput_needed; struct sockaddr_storage address; + if (flags & ~SOCK_CLOEXEC) + return -EINVAL; + + if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) + flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; + sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; @@ -1438,7 +1451,7 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, */ __module_get(newsock->ops->owner); - newfd = sock_alloc_fd(&newfile, 0); + newfd = sock_alloc_fd(&newfile, flags & O_CLOEXEC); if (unlikely(newfd < 0)) { err = newfd; sock_release(newsock); @@ -1491,6 +1504,50 @@ out_fd: goto out_put; } +asmlinkage long sys_paccept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen, + const sigset_t __user *sigmask, + size_t sigsetsize, int flags) +{ + sigset_t ksigmask, sigsaved; + int ret; + + if (sigmask) { + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask))) + return -EFAULT; + + sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + ret = do_accept(fd, upeer_sockaddr, upeer_addrlen, flags); + + if (ret < 0 && signal_pending(current)) { + /* + * Don't restore the signal mask yet. Let do_signal() deliver + * the signal on the way back to userspace, before the signal + * mask is restored. + */ + if (sigmask) { + memcpy(¤t->saved_sigmask, &sigsaved, + sizeof(sigsaved)); + set_restore_sigmask(); + } + } else if (sigmask) + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + + return ret; +} + +asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen) +{ + return do_accept(fd, upeer_sockaddr, upeer_addrlen, 0); +} + /* * Attempt to connect to a socket with the server address. The address * is in user space so we verify it is OK and move it to kernel space. @@ -2011,10 +2068,11 @@ out: /* Argument list sizes for sys_socketcall */ #define AL(x) ((x) * sizeof(unsigned long)) -static const unsigned char nargs[18]={ +static const unsigned char nargs[19]={ AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), - AL(6),AL(2),AL(5),AL(5),AL(3),AL(3) + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3), + AL(6) }; #undef AL @@ -2033,7 +2091,7 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) unsigned long a0, a1; int err; - if (call < 1 || call > SYS_RECVMSG) + if (call < 1 || call > SYS_PACCEPT) return -EINVAL; /* copy_from_user should be SMP safe. */ @@ -2062,8 +2120,8 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) break; case SYS_ACCEPT: err = - sys_accept(a0, (struct sockaddr __user *)a1, - (int __user *)a[2]); + do_accept(a0, (struct sockaddr __user *)a1, + (int __user *)a[2], 0); break; case SYS_GETSOCKNAME: err = @@ -2110,6 +2168,13 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) case SYS_RECVMSG: err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]); break; + case SYS_PACCEPT: + err = + sys_paccept(a0, (struct sockaddr __user *)a1, + (int __user *)a[2], + (const sigset_t __user *) a[3], + a[4], a[5]); + break; default: err = -EINVAL; break; -- cgit From c019bbc612f6633ede7ed67725cbf68de45ae8a4 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:21 -0700 Subject: flag parameters: paccept w/out set_restore_sigmask Some platforms do not have support to restore the signal mask in the return path from a syscall. For those platforms syscalls like pselect are not defined at all. This is, I think, not a good choice for paccept() since paccept() adds more value on top of accept() than just the signal mask handling. Therefore this patch defines a scaled down version of the sys_paccept function for those platforms. It returns -EINVAL in case the signal mask is non-NULL but behaves the same otherwise. Note that I explicitly included . I saw that it is currently included but indirectly two levels down. There is too much risk in relying on this. The header might change and then suddenly the function definition would change without anyone immediately noticing. Signed-off-by: Ulrich Drepper Cc: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/net.h | 3 +++ net/socket.c | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'include/linux') diff --git a/include/linux/net.h b/include/linux/net.h index 3a9b06d4d0fe..39a23af059b4 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -102,6 +102,9 @@ enum sock_type { /* Flags for socket, socketpair, paccept */ #define SOCK_CLOEXEC O_CLOEXEC +#ifndef SOCK_NONBLOCK +#define SOCK_NONBLOCK O_NONBLOCK +#endif #endif /* ARCH_HAS_SOCKET_TYPES */ diff --git a/net/socket.c b/net/socket.c index a0ce8ad72252..d163adff95bf 100644 --- a/net/socket.c +++ b/net/socket.c @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -1504,6 +1505,7 @@ out_fd: goto out_put; } +#ifdef HAVE_SET_RESTORE_SIGMASK asmlinkage long sys_paccept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen, const sigset_t __user *sigmask, @@ -1541,6 +1543,21 @@ asmlinkage long sys_paccept(int fd, struct sockaddr __user *upeer_sockaddr, return ret; } +#else +asmlinkage long sys_paccept(int fd, struct sockaddr __user *upeer_sockaddr, + int __user *upeer_addrlen, + const sigset_t __user *sigmask, + size_t sigsetsize, int flags) +{ + /* The platform does not support restoring the signal mask in the + * return path. So we do not allow using paccept() with a signal + * mask. */ + if (sigmask) + return -EINVAL; + + return do_accept(fd, upeer_sockaddr, upeer_addrlen, flags); +} +#endif asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen) -- cgit From 7d9dbca34240ebb6ff88d8a29c6c7bffd098f0c1 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:22 -0700 Subject: flag parameters: anon_inode_getfd extension This patch just extends the anon_inode_getfd interface to take an additional parameter with a flag value. The flag value is passed on to get_unused_fd_flags in anticipation for a use with the O_CLOEXEC flag. No actual semantic changes here, the changed callers all pass 0 for now. [akpm@linux-foundation.org: KVM fix] Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/anon_inodes.c | 9 +++++---- fs/eventfd.c | 2 +- fs/eventpoll.c | 2 +- fs/signalfd.c | 3 ++- fs/timerfd.c | 2 +- include/linux/anon_inodes.h | 2 +- virt/kvm/kvm_main.c | 4 ++-- 7 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index 977ef208c051..1a4eee620b0d 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c @@ -58,8 +58,9 @@ static struct dentry_operations anon_inodefs_dentry_operations = { * of the file * * @name: [in] name of the "class" of the new file - * @fops [in] file operations for the new file - * @priv [in] private data for the new file (will be file's private_data) + * @fops: [in] file operations for the new file + * @priv: [in] private data for the new file (will be file's private_data) + * @flags: [in] flags * * Creates a new file by hooking it on a single inode. This is useful for files * that do not need to have a full-fledged inode in order to operate correctly. @@ -68,7 +69,7 @@ static struct dentry_operations anon_inodefs_dentry_operations = { * setup. Returns new descriptor or -error. */ int anon_inode_getfd(const char *name, const struct file_operations *fops, - void *priv) + void *priv, int flags) { struct qstr this; struct dentry *dentry; @@ -78,7 +79,7 @@ int anon_inode_getfd(const char *name, const struct file_operations *fops, if (IS_ERR(anon_inode_inode)) return -ENODEV; - error = get_unused_fd(); + error = get_unused_fd_flags(flags); if (error < 0) return error; fd = error; diff --git a/fs/eventfd.c b/fs/eventfd.c index 343942deeec1..6094265ca409 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -214,7 +214,7 @@ asmlinkage long sys_eventfd(unsigned int count) * When we call this, the initialization must be complete, since * anon_inode_getfd() will install the fd. */ - fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx); + fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx, 0); if (fd < 0) kfree(ctx); return fd; diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 990c01d2d66b..9392dd968125 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1068,7 +1068,7 @@ asmlinkage long sys_epoll_create(int size) * Creates all the items needed to setup an eventpoll file. That is, * a file structure and a free file descriptor. */ - fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep); + fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep, 0); if (fd < 0) ep_free(ep); diff --git a/fs/signalfd.c b/fs/signalfd.c index 619725644c75..ddb328b74bde 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -227,7 +227,8 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas * When we call this, the initialization must be complete, since * anon_inode_getfd() will install the fd. */ - ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx); + ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx, + 0); if (ufd < 0) kfree(ctx); } else { diff --git a/fs/timerfd.c b/fs/timerfd.c index d87d354ec424..77c2bc92cbee 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -198,7 +198,7 @@ asmlinkage long sys_timerfd_create(int clockid, int flags) ctx->clockid = clockid; hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); - ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx); + ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, 0); if (ufd < 0) kfree(ctx); diff --git a/include/linux/anon_inodes.h b/include/linux/anon_inodes.h index 6129e58ca7c9..e0a0cdc2da43 100644 --- a/include/linux/anon_inodes.h +++ b/include/linux/anon_inodes.h @@ -9,7 +9,7 @@ #define _LINUX_ANON_INODES_H int anon_inode_getfd(const char *name, const struct file_operations *fops, - void *priv); + void *priv, int flags); #endif /* _LINUX_ANON_INODES_H */ diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 904d7b7bd780..a845890b6800 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -902,7 +902,7 @@ static const struct file_operations kvm_vcpu_fops = { */ static int create_vcpu_fd(struct kvm_vcpu *vcpu) { - int fd = anon_inode_getfd("kvm-vcpu", &kvm_vcpu_fops, vcpu); + int fd = anon_inode_getfd("kvm-vcpu", &kvm_vcpu_fops, vcpu, 0); if (fd < 0) kvm_put_kvm(vcpu->kvm); return fd; @@ -1261,7 +1261,7 @@ static int kvm_dev_ioctl_create_vm(void) kvm = kvm_create_vm(); if (IS_ERR(kvm)) return PTR_ERR(kvm); - fd = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm); + fd = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm, 0); if (fd < 0) kvm_put_kvm(kvm); -- cgit From 9deb27baedb79759c3ab9435a7d8b841842d56e9 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:24 -0700 Subject: flag parameters: signalfd This patch adds the new signalfd4 syscall. It extends the old signalfd syscall by one parameter which is meant to hold a flag value. In this patch the only flag support is SFD_CLOEXEC which causes the close-on-exec flag for the returned file descriptor to be set. A new name SFD_CLOEXEC is introduced which in this implementation must have the same value as O_CLOEXEC. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_signalfd4 # ifdef __x86_64__ # define __NR_signalfd4 289 # elif defined __i386__ # define __NR_signalfd4 327 # else # error "need __NR_signalfd4" # endif #endif #define SFD_CLOEXEC O_CLOEXEC int main (void) { sigset_t ss; sigemptyset (&ss); sigaddset (&ss, SIGUSR1); int fd = syscall (__NR_signalfd4, -1, &ss, 8, 0); if (fd == -1) { puts ("signalfd4(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("signalfd4(0) set close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_signalfd4, -1, &ss, 8, SFD_CLOEXEC); if (fd == -1) { puts ("signalfd4(SFD_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("signalfd4(SFD_CLOEXEC) does not set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [akpm@linux-foundation.org: add sys_ni stub] Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/kernel/syscall_table_32.S | 1 + fs/compat.c | 14 ++++++++++---- fs/signalfd.c | 14 ++++++++++++-- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/signalfd.h | 5 +++++ include/linux/syscalls.h | 1 + kernel/sys_ni.c | 1 + 9 files changed, 34 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 021d71bc69b5..c308128b9251 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -826,4 +826,5 @@ ia32_sys_call_table: .quad sys32_fallocate .quad compat_sys_timerfd_settime /* 325 */ .quad compat_sys_timerfd_gettime + .quad compat_sys_signalfd4 ia32_syscall_end: diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index adff5562f5fd..c12a36c9fd51 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -326,3 +326,4 @@ ENTRY(sys_call_table) .long sys_fallocate .long sys_timerfd_settime /* 325 */ .long sys_timerfd_gettime + .long sys_signalfd4 diff --git a/fs/compat.c b/fs/compat.c index b46604281766..106eba28ec5a 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -2131,9 +2131,9 @@ asmlinkage long compat_sys_epoll_pwait(int epfd, #ifdef CONFIG_SIGNALFD -asmlinkage long compat_sys_signalfd(int ufd, - const compat_sigset_t __user *sigmask, - compat_size_t sigsetsize) +asmlinkage long compat_sys_signalfd4(int ufd, + const compat_sigset_t __user *sigmask, + compat_size_t sigsetsize, int flags) { compat_sigset_t ss32; sigset_t tmp; @@ -2148,9 +2148,15 @@ asmlinkage long compat_sys_signalfd(int ufd, if (copy_to_user(ksigmask, &tmp, sizeof(sigset_t))) return -EFAULT; - return sys_signalfd(ufd, ksigmask, sizeof(sigset_t)); + return sys_signalfd4(ufd, ksigmask, sizeof(sigset_t), flags); } +asmlinkage long compat_sys_signalfd(int ufd, + const compat_sigset_t __user *sigmask, + compat_size_t sigsetsize) +{ + return compat_sys_signalfd4(ufd, sigmask, sigsetsize, 0); +} #endif /* CONFIG_SIGNALFD */ #ifdef CONFIG_TIMERFD diff --git a/fs/signalfd.c b/fs/signalfd.c index ddb328b74bde..c8609fa51a13 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -205,11 +205,15 @@ static const struct file_operations signalfd_fops = { .read = signalfd_read, }; -asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemask) +asmlinkage long sys_signalfd4(int ufd, sigset_t __user *user_mask, + size_t sizemask, int flags) { sigset_t sigmask; struct signalfd_ctx *ctx; + if (flags & ~SFD_CLOEXEC) + return -EINVAL; + if (sizemask != sizeof(sigset_t) || copy_from_user(&sigmask, user_mask, sizeof(sigmask))) return -EINVAL; @@ -228,7 +232,7 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas * anon_inode_getfd() will install the fd. */ ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx, - 0); + flags & O_CLOEXEC); if (ufd < 0) kfree(ctx); } else { @@ -250,3 +254,9 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas return ufd; } + +asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, + size_t sizemask) +{ + return sys_signalfd4(ufd, user_mask, sizemask, 0); +} diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index 8317d94771d3..c310371f5613 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -332,6 +332,7 @@ #define __NR_fallocate 324 #define __NR_timerfd_settime 325 #define __NR_timerfd_gettime 326 +#define __NR_signalfd4 327 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index e323994a370f..e0a9b45b2346 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -641,6 +641,8 @@ __SYSCALL(__NR_timerfd_settime, sys_timerfd_settime) __SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime) #define __NR_paccept 288 __SYSCALL(__NR_paccept, sys_paccept) +#define __NR_signalfd4 289 +__SYSCALL(__NR_signalfd4, sys_signalfd4) #ifndef __NO_STUBS diff --git a/include/linux/signalfd.h b/include/linux/signalfd.h index ea037f28df91..8b3f7b7420a1 100644 --- a/include/linux/signalfd.h +++ b/include/linux/signalfd.h @@ -8,6 +8,11 @@ #ifndef _LINUX_SIGNALFD_H #define _LINUX_SIGNALFD_H +/* For O_CLOEXEC */ +#include + +/* Flags for signalfd4. */ +#define SFD_CLOEXEC O_CLOEXEC struct signalfd_siginfo { __u32 ssi_signo; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 2a2a40af6b2c..1c2707797845 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -610,6 +610,7 @@ asmlinkage long sys_set_robust_list(struct robust_list_head __user *head, size_t len); asmlinkage long sys_getcpu(unsigned __user *cpu, unsigned __user *node, struct getcpu_cache __user *cache); asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemask); +asmlinkage long sys_signalfd4(int ufd, sigset_t __user *user_mask, size_t sizemask, int flags); asmlinkage long sys_timerfd_create(int clockid, int flags); asmlinkage long sys_timerfd_settime(int ufd, int flags, const struct itimerspec __user *utmr, diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 2f0b8a2e600f..8627c89ae9e8 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -156,6 +156,7 @@ cond_syscall(sys_ioprio_get); /* New file descriptors */ cond_syscall(sys_signalfd); +cond_syscall(sys_signalfd4); cond_syscall(compat_sys_signalfd); cond_syscall(sys_timerfd_create); cond_syscall(sys_timerfd_settime); -- cgit From b087498eb5605673b0f260a7620d91818cd72304 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:25 -0700 Subject: flag parameters: eventfd This patch adds the new eventfd2 syscall. It extends the old eventfd syscall by one parameter which is meant to hold a flag value. In this patch the only flag support is EFD_CLOEXEC which causes the close-on-exec flag for the returned file descriptor to be set. A new name EFD_CLOEXEC is introduced which in this implementation must have the same value as O_CLOEXEC. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_eventfd2 # ifdef __x86_64__ # define __NR_eventfd2 290 # elif defined __i386__ # define __NR_eventfd2 328 # else # error "need __NR_eventfd2" # endif #endif #define EFD_CLOEXEC O_CLOEXEC int main (void) { int fd = syscall (__NR_eventfd2, 1, 0); if (fd == -1) { puts ("eventfd2(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("eventfd2(0) sets close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_eventfd2, 1, EFD_CLOEXEC); if (fd == -1) { puts ("eventfd2(EFD_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("eventfd2(EFD_CLOEXEC) does not set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [akpm@linux-foundation.org: add sys_ni stub] Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/kernel/syscall_table_32.S | 1 + fs/eventfd.c | 13 +++++++++++-- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/eventfd.h | 6 ++++++ include/linux/syscalls.h | 1 + kernel/sys_ni.c | 1 + 8 files changed, 24 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index c308128b9251..cf0eb31745ca 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -827,4 +827,5 @@ ia32_sys_call_table: .quad compat_sys_timerfd_settime /* 325 */ .quad compat_sys_timerfd_gettime .quad compat_sys_signalfd4 + .quad sys_eventfd2 ia32_syscall_end: diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index c12a36c9fd51..cf112cb11c37 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -327,3 +327,4 @@ ENTRY(sys_call_table) .long sys_timerfd_settime /* 325 */ .long sys_timerfd_gettime .long sys_signalfd4 + .long sys_eventfd2 diff --git a/fs/eventfd.c b/fs/eventfd.c index 6094265ca409..bd420e6478ad 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -198,11 +198,14 @@ struct file *eventfd_fget(int fd) return file; } -asmlinkage long sys_eventfd(unsigned int count) +asmlinkage long sys_eventfd2(unsigned int count, int flags) { int fd; struct eventfd_ctx *ctx; + if (flags & ~EFD_CLOEXEC) + return -EINVAL; + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -214,9 +217,15 @@ asmlinkage long sys_eventfd(unsigned int count) * When we call this, the initialization must be complete, since * anon_inode_getfd() will install the fd. */ - fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx, 0); + fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx, + flags & O_CLOEXEC); if (fd < 0) kfree(ctx); return fd; } +asmlinkage long sys_eventfd(unsigned int count) +{ + return sys_eventfd2(count, 0); +} + diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index c310371f5613..edbd8723c939 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -333,6 +333,7 @@ #define __NR_timerfd_settime 325 #define __NR_timerfd_gettime 326 #define __NR_signalfd4 327 +#define __NR_eventfd2 328 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index e0a9b45b2346..fb059a6feeb1 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -643,6 +643,8 @@ __SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime) __SYSCALL(__NR_paccept, sys_paccept) #define __NR_signalfd4 289 __SYSCALL(__NR_signalfd4, sys_signalfd4) +#define __NR_eventfd2 290 +__SYSCALL(__NR_eventfd2, sys_eventfd2) #ifndef __NO_STUBS diff --git a/include/linux/eventfd.h b/include/linux/eventfd.h index a701399b7fed..a6c0eaedb1b0 100644 --- a/include/linux/eventfd.h +++ b/include/linux/eventfd.h @@ -10,6 +10,12 @@ #ifdef CONFIG_EVENTFD +/* For O_CLOEXEC */ +#include + +/* Flags for eventfd2. */ +#define EFD_CLOEXEC O_CLOEXEC + struct file *eventfd_fget(int fd); int eventfd_signal(struct file *file, int n); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 1c2707797845..9ab09926a7f2 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -617,6 +617,7 @@ asmlinkage long sys_timerfd_settime(int ufd, int flags, struct itimerspec __user *otmr); asmlinkage long sys_timerfd_gettime(int ufd, struct itimerspec __user *otmr); asmlinkage long sys_eventfd(unsigned int count); +asmlinkage long sys_eventfd2(unsigned int count, int flags); asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len); int kernel_execve(const char *filename, char *const argv[], char *const envp[]); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 8627c89ae9e8..2a361ccdc7ca 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -164,3 +164,4 @@ cond_syscall(sys_timerfd_gettime); cond_syscall(compat_sys_timerfd_settime); cond_syscall(compat_sys_timerfd_gettime); cond_syscall(sys_eventfd); +cond_syscall(sys_eventfd2); -- cgit From 11fcb6c14676023d0bd437841f5dcd670e7990a0 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:26 -0700 Subject: flag parameters: timerfd_create The timerfd_create syscall already has a flags parameter. It just is unused so far. This patch changes this by introducing the TFD_CLOEXEC flag to set the close-on-exec flag for the returned file descriptor. A new name TFD_CLOEXEC is introduced which in this implementation must have the same value as O_CLOEXEC. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_timerfd_create # ifdef __x86_64__ # define __NR_timerfd_create 283 # elif defined __i386__ # define __NR_timerfd_create 322 # else # error "need __NR_timerfd_create" # endif #endif #define TFD_CLOEXEC O_CLOEXEC int main (void) { int fd = syscall (__NR_timerfd_create, CLOCK_REALTIME, 0); if (fd == -1) { puts ("timerfd_create(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("timerfd_create(0) set close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_timerfd_create, CLOCK_REALTIME, TFD_CLOEXEC); if (fd == -1) { puts ("timerfd_create(TFD_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("timerfd_create(TFD_CLOEXEC) set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/timerfd.c | 5 +++-- include/linux/timerfd.h | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/timerfd.c b/fs/timerfd.c index 77c2bc92cbee..c6ef5e33cb34 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -184,7 +184,7 @@ asmlinkage long sys_timerfd_create(int clockid, int flags) int ufd; struct timerfd_ctx *ctx; - if (flags) + if (flags & ~TFD_CLOEXEC) return -EINVAL; if (clockid != CLOCK_MONOTONIC && clockid != CLOCK_REALTIME) @@ -198,7 +198,8 @@ asmlinkage long sys_timerfd_create(int clockid, int flags) ctx->clockid = clockid; hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); - ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, 0); + ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, + flags & O_CLOEXEC); if (ufd < 0) kfree(ctx); diff --git a/include/linux/timerfd.h b/include/linux/timerfd.h index cf2b10d75731..96ed97dff00f 100644 --- a/include/linux/timerfd.h +++ b/include/linux/timerfd.h @@ -8,9 +8,14 @@ #ifndef _LINUX_TIMERFD_H #define _LINUX_TIMERFD_H +/* For O_CLOEXEC */ +#include +/* Flags for timerfd_settime. */ #define TFD_TIMER_ABSTIME (1 << 0) +/* Flags for timerfd_create. */ +#define TFD_CLOEXEC O_CLOEXEC #endif /* _LINUX_TIMERFD_H */ -- cgit From a0998b50c3f0b8fdd265c63e0032f86ebe377dbf Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:27 -0700 Subject: flag parameters: epoll_create This patch adds the new epoll_create2 syscall. It extends the old epoll_create syscall by one parameter which is meant to hold a flag value. In this patch the only flag support is EPOLL_CLOEXEC which causes the close-on-exec flag for the returned file descriptor to be set. A new name EPOLL_CLOEXEC is introduced which in this implementation must have the same value as O_CLOEXEC. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_epoll_create2 # ifdef __x86_64__ # define __NR_epoll_create2 291 # elif defined __i386__ # define __NR_epoll_create2 329 # else # error "need __NR_epoll_create2" # endif #endif #define EPOLL_CLOEXEC O_CLOEXEC int main (void) { int fd = syscall (__NR_epoll_create2, 1, 0); if (fd == -1) { puts ("epoll_create2(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("epoll_create2(0) set close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_epoll_create2, 1, EPOLL_CLOEXEC); if (fd == -1) { puts ("epoll_create2(EPOLL_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("epoll_create2(EPOLL_CLOEXEC) set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/kernel/syscall_table_32.S | 1 + fs/eventpoll.c | 13 +++++++++++-- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/eventpoll.h | 4 ++++ include/linux/syscalls.h | 1 + 7 files changed, 21 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index cf0eb31745ca..04366f08f424 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -828,4 +828,5 @@ ia32_sys_call_table: .quad compat_sys_timerfd_gettime .quad compat_sys_signalfd4 .quad sys_eventfd2 + .quad sys_epoll_create2 ia32_syscall_end: diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index cf112cb11c37..4d7007ca263d 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -328,3 +328,4 @@ ENTRY(sys_call_table) .long sys_timerfd_gettime .long sys_signalfd4 .long sys_eventfd2 + .long sys_epoll_create2 diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 9392dd968125..3fd4014f3c5a 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1046,11 +1046,14 @@ retry: * RB tree. With the current implementation, the "size" parameter is ignored * (besides sanity checks). */ -asmlinkage long sys_epoll_create(int size) +asmlinkage long sys_epoll_create2(int size, int flags) { int error, fd = -1; struct eventpoll *ep; + if (flags & ~EPOLL_CLOEXEC) + return -EINVAL; + DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d)\n", current, size)); @@ -1068,7 +1071,8 @@ asmlinkage long sys_epoll_create(int size) * Creates all the items needed to setup an eventpoll file. That is, * a file structure and a free file descriptor. */ - fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep, 0); + fd = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep, + flags & O_CLOEXEC); if (fd < 0) ep_free(ep); @@ -1079,6 +1083,11 @@ error_return: return fd; } +asmlinkage long sys_epoll_create(int size) +{ + return sys_epoll_create2(size, 0); +} + /* * The following function implements the controller interface for * the eventpoll file that enables the insertion/removal/change of diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index edbd8723c939..a37d6b0c4e1e 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -334,6 +334,7 @@ #define __NR_timerfd_gettime 326 #define __NR_signalfd4 327 #define __NR_eventfd2 328 +#define __NR_epoll_create2 329 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index fb059a6feeb1..a1a4a5b6e5ee 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -645,6 +645,8 @@ __SYSCALL(__NR_paccept, sys_paccept) __SYSCALL(__NR_signalfd4, sys_signalfd4) #define __NR_eventfd2 290 __SYSCALL(__NR_eventfd2, sys_eventfd2) +#define __NR_epoll_create2 291 +__SYSCALL(__NR_epoll_create2, sys_epoll_create2) #ifndef __NO_STUBS diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index cf79853967ff..1cfaa40059c8 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -14,8 +14,12 @@ #ifndef _LINUX_EVENTPOLL_H #define _LINUX_EVENTPOLL_H +/* For O_CLOEXEC */ +#include #include +/* Flags for epoll_create2. */ +#define EPOLL_CLOEXEC O_CLOEXEC /* Valid opcodes to issue to sys_epoll_ctl() */ #define EPOLL_CTL_ADD 1 diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 9ab09926a7f2..85953240f28c 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -430,6 +430,7 @@ asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp); asmlinkage long sys_epoll_create(int size); +asmlinkage long sys_epoll_create2(int size, int flags); asmlinkage long sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event); asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events, -- cgit From 336dd1f70ff62d7dd8655228caed4c5bfc818c56 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:29 -0700 Subject: flag parameters: dup2 This patch adds the new dup3 syscall. It extends the old dup2 syscall by one parameter which is meant to hold a flag value. Support for the O_CLOEXEC flag is added in this patch. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_dup3 # ifdef __x86_64__ # define __NR_dup3 292 # elif defined __i386__ # define __NR_dup3 330 # else # error "need __NR_dup3" # endif #endif int main (void) { int fd = syscall (__NR_dup3, 1, 4, 0); if (fd == -1) { puts ("dup3(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("dup3(0) set close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_dup3, 1, 4, O_CLOEXEC); if (fd == -1) { puts ("dup3(O_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("dup3(O_CLOEXEC) set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/kernel/syscall_table_32.S | 1 + fs/fcntl.c | 15 +++++++++++++-- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/syscalls.h | 1 + 6 files changed, 19 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 04366f08f424..5614a8f7bed4 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -829,4 +829,5 @@ ia32_sys_call_table: .quad compat_sys_signalfd4 .quad sys_eventfd2 .quad sys_epoll_create2 + .quad sys_dup3 /* 330 */ ia32_syscall_end: diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index 4d7007ca263d..24a3f1ea6a0e 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -329,3 +329,4 @@ ENTRY(sys_call_table) .long sys_signalfd4 .long sys_eventfd2 .long sys_epoll_create2 + .long sys_dup3 /* 330 */ diff --git a/fs/fcntl.c b/fs/fcntl.c index 330a7d782591..9679fcbdeaa0 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -125,13 +125,16 @@ static int dupfd(struct file *file, unsigned int start, int cloexec) return fd; } -asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) +asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags) { int err = -EBADF; struct file * file, *tofree; struct files_struct * files = current->files; struct fdtable *fdt; + if ((flags & ~O_CLOEXEC) != 0) + return -EINVAL; + spin_lock(&files->file_lock); if (!(file = fcheck(oldfd))) goto out_unlock; @@ -163,7 +166,10 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) rcu_assign_pointer(fdt->fd[newfd], file); FD_SET(newfd, fdt->open_fds); - FD_CLR(newfd, fdt->close_on_exec); + if (flags & O_CLOEXEC) + FD_SET(newfd, fdt->close_on_exec); + else + FD_CLR(newfd, fdt->close_on_exec); spin_unlock(&files->file_lock); if (tofree) @@ -181,6 +187,11 @@ out_fput: goto out; } +asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) +{ + return sys_dup3(oldfd, newfd, 0); +} + asmlinkage long sys_dup(unsigned int fildes) { int ret = -EBADF; diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index a37d6b0c4e1e..a1f6383bf695 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -335,6 +335,7 @@ #define __NR_signalfd4 327 #define __NR_eventfd2 328 #define __NR_epoll_create2 329 +#define __NR_dup3 330 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index a1a4a5b6e5ee..f0fb2bd40cdb 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -647,6 +647,8 @@ __SYSCALL(__NR_signalfd4, sys_signalfd4) __SYSCALL(__NR_eventfd2, sys_eventfd2) #define __NR_epoll_create2 291 __SYSCALL(__NR_epoll_create2, sys_epoll_create2) +#define __NR_dup3 292 +__SYSCALL(__NR_dup3, sys_dup3) #ifndef __NO_STUBS diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 85953240f28c..034d3358549e 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -305,6 +305,7 @@ asmlinkage long sys_fcntl64(unsigned int fd, #endif asmlinkage long sys_dup(unsigned int fildes); asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd); +asmlinkage long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags); asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); -- cgit From ed8cae8ba01348bfd83333f4648dd807b04d7f08 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:30 -0700 Subject: flag parameters: pipe This patch introduces the new syscall pipe2 which is like pipe but it also takes an additional parameter which takes a flag value. This patch implements the handling of O_CLOEXEC for the flag. I did not add support for the new syscall for the architectures which have a special sys_pipe implementation. I think the maintainers of those archs have the chance to go with the unified implementation but that's up to them. The implementation introduces do_pipe_flags. I did that instead of changing all callers of do_pipe because some of the callers are written in assembler. I would probably screw up changing the assembly code. To avoid breaking code do_pipe is now a small wrapper around do_pipe_flags. Once all callers are changed over to do_pipe_flags the old do_pipe function can be removed. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_pipe2 # ifdef __x86_64__ # define __NR_pipe2 293 # elif defined __i386__ # define __NR_pipe2 331 # else # error "need __NR_pipe2" # endif #endif int main (void) { int fd[2]; if (syscall (__NR_pipe2, fd, 0) != 0) { puts ("pipe2(0) failed"); return 1; } for (int i = 0; i < 2; ++i) { int coe = fcntl (fd[i], F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { printf ("pipe2(0) set close-on-exit for fd[%d]\n", i); return 1; } } close (fd[0]); close (fd[1]); if (syscall (__NR_pipe2, fd, O_CLOEXEC) != 0) { puts ("pipe2(O_CLOEXEC) failed"); return 1; } for (int i = 0; i < 2; ++i) { int coe = fcntl (fd[i], F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { printf ("pipe2(O_CLOEXEC) does not set close-on-exit for fd[%d]\n", i); return 1; } } close (fd[0]); close (fd[1]); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/ia32/sys_ia32.c | 2 +- arch/ia64/kernel/sys_ia64.c | 2 +- arch/mips/kernel/syscall.c | 2 +- arch/parisc/hpux/sys_hpux.c | 2 +- arch/sh/kernel/sys_sh32.c | 2 +- arch/sparc/kernel/sys_sparc.c | 2 +- arch/sparc64/kernel/sys_sparc.c | 2 +- arch/x86/ia32/ia32entry.S | 1 + arch/x86/ia32/sys_ia32.c | 2 +- arch/x86/kernel/syscall_table_32.S | 1 + arch/xtensa/kernel/syscall.c | 2 +- fs/pipe.c | 23 ++++++++++++++++++----- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/fs.h | 1 + 15 files changed, 33 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 7e028ceb93ba..465116aecb85 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -1139,7 +1139,7 @@ sys32_pipe (int __user *fd) int retval; int fds[2]; - retval = do_pipe(fds); + retval = do_pipe_flags(fds, 0); if (retval) goto out; if (copy_to_user(fd, fds, sizeof(fds))) diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c index 1eda194b9559..bcbb6d8792d3 100644 --- a/arch/ia64/kernel/sys_ia64.c +++ b/arch/ia64/kernel/sys_ia64.c @@ -160,7 +160,7 @@ sys_pipe (void) int fd[2]; int retval; - retval = do_pipe(fd); + retval = do_pipe_flags(fd, 0); if (retval) goto out; retval = fd[0]; diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 3523c8d12eda..343015a2f418 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -52,7 +52,7 @@ asmlinkage int sysm_pipe(nabi_no_regargs volatile struct pt_regs regs) int fd[2]; int error, res; - error = do_pipe(fd); + error = do_pipe_flags(fd, 0); if (error) { res = error; goto out; diff --git a/arch/parisc/hpux/sys_hpux.c b/arch/parisc/hpux/sys_hpux.c index 0c5b9dabb475..be255ebb609c 100644 --- a/arch/parisc/hpux/sys_hpux.c +++ b/arch/parisc/hpux/sys_hpux.c @@ -448,7 +448,7 @@ int hpux_pipe(int *kstack_fildes) int error; lock_kernel(); - error = do_pipe(kstack_fildes); + error = do_pipe_flags(kstack_fildes, 0); unlock_kernel(); return error; } diff --git a/arch/sh/kernel/sys_sh32.c b/arch/sh/kernel/sys_sh32.c index 125e493ead82..f0aa5c398656 100644 --- a/arch/sh/kernel/sys_sh32.c +++ b/arch/sh/kernel/sys_sh32.c @@ -29,7 +29,7 @@ asmlinkage int sys_pipe(unsigned long r4, unsigned long r5, int fd[2]; int error; - error = do_pipe(fd); + error = do_pipe_flags(fd, 0); if (!error) { regs->regs[1] = fd[1]; return fd[0]; diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index 3c6b49a53ae8..4d73421559c3 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c @@ -97,7 +97,7 @@ asmlinkage int sparc_pipe(struct pt_regs *regs) int fd[2]; int error; - error = do_pipe(fd); + error = do_pipe_flags(fd, 0); if (error) goto out; regs->u_regs[UREG_I1] = fd[1]; diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index e1f4eba2e576..39749e32dc7e 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -418,7 +418,7 @@ asmlinkage long sparc_pipe(struct pt_regs *regs) int fd[2]; int error; - error = do_pipe(fd); + error = do_pipe_flags(fd, 0); if (error) goto out; regs->u_regs[UREG_I1] = fd[1]; diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 5614a8f7bed4..18808b164570 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -830,4 +830,5 @@ ia32_sys_call_table: .quad sys_eventfd2 .quad sys_epoll_create2 .quad sys_dup3 /* 330 */ + .quad sys_pipe2 ia32_syscall_end: diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c index f00afdf61e67..d3c64088b981 100644 --- a/arch/x86/ia32/sys_ia32.c +++ b/arch/x86/ia32/sys_ia32.c @@ -238,7 +238,7 @@ asmlinkage long sys32_pipe(int __user *fd) int retval; int fds[2]; - retval = do_pipe(fds); + retval = do_pipe_flags(fds, 0); if (retval) goto out; if (copy_to_user(fd, fds, sizeof(fds))) diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index 24a3f1ea6a0e..66154769d52f 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -330,3 +330,4 @@ ENTRY(sys_call_table) .long sys_eventfd2 .long sys_epoll_create2 .long sys_dup3 /* 330 */ + .long sys_pipe2 diff --git a/arch/xtensa/kernel/syscall.c b/arch/xtensa/kernel/syscall.c index f3e16efcd47a..ac15ecbdf919 100644 --- a/arch/xtensa/kernel/syscall.c +++ b/arch/xtensa/kernel/syscall.c @@ -49,7 +49,7 @@ asmlinkage long xtensa_pipe(int __user *userfds) int fd[2]; int error; - error = do_pipe(fd); + error = do_pipe_flags(fd, 0); if (!error) { if (copy_to_user(userfds, fd, 2 * sizeof(int))) error = -EFAULT; diff --git a/fs/pipe.c b/fs/pipe.c index 700f4e0d9572..68e82061070c 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1027,12 +1027,15 @@ struct file *create_read_pipe(struct file *wrf) return f; } -int do_pipe(int *fd) +int do_pipe_flags(int *fd, int flags) { struct file *fw, *fr; int error; int fdw, fdr; + if (flags & ~O_CLOEXEC) + return -EINVAL; + fw = create_write_pipe(); if (IS_ERR(fw)) return PTR_ERR(fw); @@ -1041,12 +1044,12 @@ int do_pipe(int *fd) if (IS_ERR(fr)) goto err_write_pipe; - error = get_unused_fd(); + error = get_unused_fd_flags(flags); if (error < 0) goto err_read_pipe; fdr = error; - error = get_unused_fd(); + error = get_unused_fd_flags(flags); if (error < 0) goto err_fdr; fdw = error; @@ -1074,16 +1077,21 @@ int do_pipe(int *fd) return error; } +int do_pipe(int *fd) +{ + return do_pipe_flags(fd, 0); +} + /* * sys_pipe() is the normal C calling standard for creating * a pipe. It's not the way Unix traditionally does this, though. */ -asmlinkage long __weak sys_pipe(int __user *fildes) +asmlinkage long __weak sys_pipe2(int __user *fildes, int flags) { int fd[2]; int error; - error = do_pipe(fd); + error = do_pipe_flags(fd, flags); if (!error) { if (copy_to_user(fildes, fd, sizeof(fd))) { sys_close(fd[0]); @@ -1094,6 +1102,11 @@ asmlinkage long __weak sys_pipe(int __user *fildes) return error; } +asmlinkage long __weak sys_pipe(int __user *fildes) +{ + return sys_pipe2(fildes, 0); +} + /* * pipefs should _never_ be mounted by userland - too much of security hassle, * no real gain from having the whole whorehouse mounted. So we don't need diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index a1f6383bf695..748a05c77da4 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -336,6 +336,7 @@ #define __NR_eventfd2 328 #define __NR_epoll_create2 329 #define __NR_dup3 330 +#define __NR_pipe2 331 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index f0fb2bd40cdb..d2284b43ad58 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -649,6 +649,8 @@ __SYSCALL(__NR_eventfd2, sys_eventfd2) __SYSCALL(__NR_epoll_create2, sys_epoll_create2) #define __NR_dup3 292 __SYSCALL(__NR_dup3, sys_dup3) +#define __NR_pipe2 293 +__SYSCALL(__NR_pipe2, sys_pipe2) #ifndef __NO_STUBS diff --git a/include/linux/fs.h b/include/linux/fs.h index e5e6a244096c..0e80cd717d32 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1777,6 +1777,7 @@ static inline void allow_write_access(struct file *file) atomic_inc(&file->f_path.dentry->d_inode->i_writecount); } extern int do_pipe(int *); +extern int do_pipe_flags(int *, int); extern struct file *create_read_pipe(struct file *f); extern struct file *create_write_pipe(void); extern void free_write_pipe(struct file *); -- cgit From 4006553b06306b34054529477b06b68a1c66249b Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:32 -0700 Subject: flag parameters: inotify_init This patch introduces the new syscall inotify_init1 (note: the 1 stands for the one parameter the syscall takes, as opposed to no parameter before). The values accepted for this parameter are function-specific and defined in the inotify.h header. Here the values must match the O_* flags, though. In this patch CLOEXEC support is introduced. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_inotify_init1 # ifdef __x86_64__ # define __NR_inotify_init1 294 # elif defined __i386__ # define __NR_inotify_init1 332 # else # error "need __NR_inotify_init1" # endif #endif #define IN_CLOEXEC O_CLOEXEC int main (void) { int fd; fd = syscall (__NR_inotify_init1, 0); if (fd == -1) { puts ("inotify_init1(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("inotify_init1(0) set close-on-exit"); return 1; } close (fd); fd = syscall (__NR_inotify_init1, IN_CLOEXEC); if (fd == -1) { puts ("inotify_init1(IN_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("inotify_init1(O_CLOEXEC) does not set close-on-exit"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [akpm@linux-foundation.org: add sys_ni stub] Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/kernel/syscall_table_32.S | 1 + fs/inotify_user.c | 12 ++++++++++-- include/asm-x86/unistd_32.h | 1 + include/asm-x86/unistd_64.h | 2 ++ include/linux/inotify.h | 5 +++++ include/linux/syscalls.h | 1 + kernel/sys_ni.c | 1 + 8 files changed, 22 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 18808b164570..4541073dd837 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -831,4 +831,5 @@ ia32_sys_call_table: .quad sys_epoll_create2 .quad sys_dup3 /* 330 */ .quad sys_pipe2 + .quad sys_inotify_init1 ia32_syscall_end: diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index 66154769d52f..f59aba5ff0f0 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -331,3 +331,4 @@ ENTRY(sys_call_table) .long sys_epoll_create2 .long sys_dup3 /* 330 */ .long sys_pipe2 + .long sys_inotify_init1 diff --git a/fs/inotify_user.c b/fs/inotify_user.c index 6676c06bb7c1..851005998cd4 100644 --- a/fs/inotify_user.c +++ b/fs/inotify_user.c @@ -566,7 +566,7 @@ static const struct inotify_operations inotify_user_ops = { .destroy_watch = free_inotify_user_watch, }; -asmlinkage long sys_inotify_init(void) +asmlinkage long sys_inotify_init1(int flags) { struct inotify_device *dev; struct inotify_handle *ih; @@ -574,7 +574,10 @@ asmlinkage long sys_inotify_init(void) struct file *filp; int fd, ret; - fd = get_unused_fd(); + if (flags & ~IN_CLOEXEC) + return -EINVAL; + + fd = get_unused_fd_flags(flags & O_CLOEXEC); if (fd < 0) return fd; @@ -638,6 +641,11 @@ out_put_fd: return ret; } +asmlinkage long sys_inotify_init(void) +{ + return sys_inotify_init1(0); +} + asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) { struct inode *inode; diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index 748a05c77da4..b3daf503ab93 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -337,6 +337,7 @@ #define __NR_epoll_create2 329 #define __NR_dup3 330 #define __NR_pipe2 331 +#define __NR_inotify_init1 332 #ifdef __KERNEL__ diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index d2284b43ad58..c8cb88d70c6b 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -651,6 +651,8 @@ __SYSCALL(__NR_epoll_create2, sys_epoll_create2) __SYSCALL(__NR_dup3, sys_dup3) #define __NR_pipe2 293 __SYSCALL(__NR_pipe2, sys_pipe2) +#define __NR_inotify_init1 294 +__SYSCALL(__NR_inotify_init1, sys_inotify_init1) #ifndef __NO_STUBS diff --git a/include/linux/inotify.h b/include/linux/inotify.h index 742b917e7d1b..72ef82120512 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -7,6 +7,8 @@ #ifndef _LINUX_INOTIFY_H #define _LINUX_INOTIFY_H +/* For O_CLOEXEC */ +#include #include /* @@ -63,6 +65,9 @@ struct inotify_event { IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF | \ IN_MOVE_SELF) +/* Flags for sys_inotify_init1. */ +#define IN_CLOEXEC O_CLOEXEC + #ifdef __KERNEL__ #include diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 034d3358549e..93a7e7f017a6 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -547,6 +547,7 @@ asmlinkage long sys_get_mempolicy(int __user *policy, unsigned long addr, unsigned long flags); asmlinkage long sys_inotify_init(void); +asmlinkage long sys_inotify_init1(int flags); asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask); asmlinkage long sys_inotify_rm_watch(int fd, u32 wd); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 2a361ccdc7ca..bd66ac5406f3 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -96,6 +96,7 @@ cond_syscall(sys_keyctl); cond_syscall(compat_sys_keyctl); cond_syscall(compat_sys_socketcall); cond_syscall(sys_inotify_init); +cond_syscall(sys_inotify_init1); cond_syscall(sys_inotify_add_watch); cond_syscall(sys_inotify_rm_watch); cond_syscall(sys_migrate_pages); -- cgit From 77d2720059618b9b6e827a8b73831eb6c6fad63c Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:35 -0700 Subject: flag parameters: NONBLOCK in socket and socketpair This patch introduces support for the SOCK_NONBLOCK flag in socket, socketpair, and paccept. To do this the internal function sock_attach_fd gets an additional parameter which it uses to set the appropriate flag for the file descriptor. Given that in modern, scalable programs almost all socket connections are non-blocking and the minimal additional cost for the new functionality I see no reason not to add this code. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #include #include #ifndef __NR_paccept # ifdef __x86_64__ # define __NR_paccept 288 # elif defined __i386__ # define SYS_PACCEPT 18 # define USE_SOCKETCALL 1 # else # error "need __NR_paccept" # endif #endif #ifdef USE_SOCKETCALL # define paccept(fd, addr, addrlen, mask, flags) \ ({ long args[6] = { \ (long) fd, (long) addr, (long) addrlen, (long) mask, 8, (long) flags }; \ syscall (__NR_socketcall, SYS_PACCEPT, args); }) #else # define paccept(fd, addr, addrlen, mask, flags) \ syscall (__NR_paccept, fd, addr, addrlen, mask, 8, flags) #endif #define PORT 57392 #define SOCK_NONBLOCK O_NONBLOCK static pthread_barrier_t b; static void * tf (void *arg) { pthread_barrier_wait (&b); int s = socket (AF_INET, SOCK_STREAM, 0); struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); sin.sin_port = htons (PORT); connect (s, (const struct sockaddr *) &sin, sizeof (sin)); close (s); pthread_barrier_wait (&b); pthread_barrier_wait (&b); s = socket (AF_INET, SOCK_STREAM, 0); sin.sin_port = htons (PORT); connect (s, (const struct sockaddr *) &sin, sizeof (sin)); close (s); pthread_barrier_wait (&b); return NULL; } int main (void) { int fd; fd = socket (PF_INET, SOCK_STREAM, 0); if (fd == -1) { puts ("socket(0) failed"); return 1; } int fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { puts ("socket(0) set non-blocking mode"); return 1; } close (fd); fd = socket (PF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0); if (fd == -1) { puts ("socket(SOCK_NONBLOCK) failed"); return 1; } fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { puts ("socket(SOCK_NONBLOCK) does not set non-blocking mode"); return 1; } close (fd); int fds[2]; if (socketpair (PF_UNIX, SOCK_STREAM, 0, fds) == -1) { puts ("socketpair(0) failed"); return 1; } for (int i = 0; i < 2; ++i) { fl = fcntl (fds[i], F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { printf ("socketpair(0) set non-blocking mode for fds[%d]\n", i); return 1; } close (fds[i]); } if (socketpair (PF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0, fds) == -1) { puts ("socketpair(SOCK_NONBLOCK) failed"); return 1; } for (int i = 0; i < 2; ++i) { fl = fcntl (fds[i], F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { printf ("socketpair(SOCK_NONBLOCK) does not set non-blocking mode for fds[%d]\n", i); return 1; } close (fds[i]); } pthread_barrier_init (&b, NULL, 2); struct sockaddr_in sin; pthread_t th; if (pthread_create (&th, NULL, tf, NULL) != 0) { puts ("pthread_create failed"); return 1; } int s = socket (AF_INET, SOCK_STREAM, 0); int reuse = 1; setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); sin.sin_port = htons (PORT); bind (s, (struct sockaddr *) &sin, sizeof (sin)); listen (s, SOMAXCONN); pthread_barrier_wait (&b); int s2 = paccept (s, NULL, 0, NULL, 0); if (s2 < 0) { puts ("paccept(0) failed"); return 1; } fl = fcntl (s2, F_GETFL); if (fl & O_NONBLOCK) { puts ("paccept(0) set non-blocking mode"); return 1; } close (s2); close (s); pthread_barrier_wait (&b); s = socket (AF_INET, SOCK_STREAM, 0); sin.sin_port = htons (PORT); setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse)); bind (s, (struct sockaddr *) &sin, sizeof (sin)); listen (s, SOMAXCONN); pthread_barrier_wait (&b); s2 = paccept (s, NULL, 0, NULL, SOCK_NONBLOCK); if (s2 < 0) { puts ("paccept(SOCK_NONBLOCK) failed"); return 1; } fl = fcntl (s2, F_GETFL); if ((fl & O_NONBLOCK) == 0) { puts ("paccept(SOCK_NONBLOCK) does not set non-blocking mode"); return 1; } close (s2); close (s); pthread_barrier_wait (&b); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/net.h | 2 +- net/socket.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/net.h b/include/linux/net.h index 39a23af059b4..2f999fbb188d 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -20,7 +20,7 @@ #include #include -#include /* For O_CLOEXEC */ +#include /* For O_CLOEXEC and O_NONBLOCK */ #include struct poll_table_struct; diff --git a/net/socket.c b/net/socket.c index d163adff95bf..31105f9048a8 100644 --- a/net/socket.c +++ b/net/socket.c @@ -369,7 +369,7 @@ static int sock_alloc_fd(struct file **filep, int flags) return fd; } -static int sock_attach_fd(struct socket *sock, struct file *file) +static int sock_attach_fd(struct socket *sock, struct file *file, int flags) { struct dentry *dentry; struct qstr name = { .name = "" }; @@ -391,7 +391,7 @@ static int sock_attach_fd(struct socket *sock, struct file *file) init_file(file, sock_mnt, dentry, FMODE_READ | FMODE_WRITE, &socket_file_ops); SOCK_INODE(sock)->i_fop = &socket_file_ops; - file->f_flags = O_RDWR; + file->f_flags = O_RDWR | (flags & O_NONBLOCK); file->f_pos = 0; file->private_data = sock; @@ -404,7 +404,7 @@ int sock_map_fd(struct socket *sock, int flags) int fd = sock_alloc_fd(&newfile, flags); if (likely(fd >= 0)) { - int err = sock_attach_fd(sock, newfile); + int err = sock_attach_fd(sock, newfile, flags); if (unlikely(err < 0)) { put_filp(newfile); @@ -1223,7 +1223,7 @@ asmlinkage long sys_socket(int family, int type, int protocol) int flags; flags = type & ~SOCK_TYPE_MASK; - if (flags & ~SOCK_CLOEXEC) + if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= SOCK_TYPE_MASK; @@ -1234,7 +1234,7 @@ asmlinkage long sys_socket(int family, int type, int protocol) if (retval < 0) goto out; - retval = sock_map_fd(sock, flags & O_CLOEXEC); + retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK)); if (retval < 0) goto out_release; @@ -1260,7 +1260,7 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, int flags; flags = type & ~SOCK_TYPE_MASK; - if (flags & ~SOCK_CLOEXEC) + if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= SOCK_TYPE_MASK; @@ -1298,12 +1298,12 @@ asmlinkage long sys_socketpair(int family, int type, int protocol, goto out_release_both; } - err = sock_attach_fd(sock1, newfile1); + err = sock_attach_fd(sock1, newfile1, flags & O_NONBLOCK); if (unlikely(err < 0)) { goto out_fd2; } - err = sock_attach_fd(sock2, newfile2); + err = sock_attach_fd(sock2, newfile2, flags & O_NONBLOCK); if (unlikely(err < 0)) { fput(newfile1); goto out_fd1; @@ -1429,7 +1429,7 @@ long do_accept(int fd, struct sockaddr __user *upeer_sockaddr, int err, len, newfd, fput_needed; struct sockaddr_storage address; - if (flags & ~SOCK_CLOEXEC) + if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) @@ -1459,7 +1459,7 @@ long do_accept(int fd, struct sockaddr __user *upeer_sockaddr, goto out_put; } - err = sock_attach_fd(newsock, newfile); + err = sock_attach_fd(newsock, newfile, flags & O_NONBLOCK); if (err < 0) goto out_fd_simple; -- cgit From 5fb5e04926a54bc1c22bba7ca166840f4476196f Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:37 -0700 Subject: flag parameters: NONBLOCK in signalfd This patch adds support for the SFD_NONBLOCK flag to signalfd4. The additional changes needed are minimal. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_signalfd4 # ifdef __x86_64__ # define __NR_signalfd4 289 # elif defined __i386__ # define __NR_signalfd4 327 # else # error "need __NR_signalfd4" # endif #endif #define SFD_NONBLOCK O_NONBLOCK int main (void) { sigset_t ss; sigemptyset (&ss); sigaddset (&ss, SIGUSR1); int fd = syscall (__NR_signalfd4, -1, &ss, 8, 0); if (fd == -1) { puts ("signalfd4(0) failed"); return 1; } int fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { puts ("signalfd4(0) set non-blocking mode"); return 1; } close (fd); fd = syscall (__NR_signalfd4, -1, &ss, 8, SFD_NONBLOCK); if (fd == -1) { puts ("signalfd4(SFD_NONBLOCK) failed"); return 1; } fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { puts ("signalfd4(SFD_NONBLOCK) does not set non-blocking mode"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/signalfd.c | 4 ++-- include/linux/signalfd.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/signalfd.c b/fs/signalfd.c index c8609fa51a13..5441a4bca772 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -211,7 +211,7 @@ asmlinkage long sys_signalfd4(int ufd, sigset_t __user *user_mask, sigset_t sigmask; struct signalfd_ctx *ctx; - if (flags & ~SFD_CLOEXEC) + if (flags & ~(SFD_CLOEXEC | SFD_NONBLOCK)) return -EINVAL; if (sizemask != sizeof(sigset_t) || @@ -232,7 +232,7 @@ asmlinkage long sys_signalfd4(int ufd, sigset_t __user *user_mask, * anon_inode_getfd() will install the fd. */ ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx, - flags & O_CLOEXEC); + flags & (O_CLOEXEC | O_NONBLOCK)); if (ufd < 0) kfree(ctx); } else { diff --git a/include/linux/signalfd.h b/include/linux/signalfd.h index 8b3f7b7420a1..bef0c46d4713 100644 --- a/include/linux/signalfd.h +++ b/include/linux/signalfd.h @@ -8,11 +8,12 @@ #ifndef _LINUX_SIGNALFD_H #define _LINUX_SIGNALFD_H -/* For O_CLOEXEC */ +/* For O_CLOEXEC and O_NONBLOCK */ #include /* Flags for signalfd4. */ #define SFD_CLOEXEC O_CLOEXEC +#define SFD_NONBLOCK O_NONBLOCK struct signalfd_siginfo { __u32 ssi_signo; -- cgit From e7d476dfdf0bcfed478a207aecfdc84f81efecaf Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:38 -0700 Subject: flag parameters: NONBLOCK in eventfd This patch adds support for the EFD_NONBLOCK flag to eventfd2. The additional changes needed are minimal. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_eventfd2 # ifdef __x86_64__ # define __NR_eventfd2 290 # elif defined __i386__ # define __NR_eventfd2 328 # else # error "need __NR_eventfd2" # endif #endif #define EFD_NONBLOCK O_NONBLOCK int main (void) { int fd = syscall (__NR_eventfd2, 1, 0); if (fd == -1) { puts ("eventfd2(0) failed"); return 1; } int fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { puts ("eventfd2(0) sets non-blocking mode"); return 1; } close (fd); fd = syscall (__NR_eventfd2, 1, EFD_NONBLOCK); if (fd == -1) { puts ("eventfd2(EFD_NONBLOCK) failed"); return 1; } fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { puts ("eventfd2(EFD_NONBLOCK) does not set non-blocking mode"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/eventfd.c | 4 ++-- include/linux/eventfd.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/eventfd.c b/fs/eventfd.c index bd420e6478ad..3ed4466177a7 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -203,7 +203,7 @@ asmlinkage long sys_eventfd2(unsigned int count, int flags) int fd; struct eventfd_ctx *ctx; - if (flags & ~EFD_CLOEXEC) + if (flags & ~(EFD_CLOEXEC | EFD_NONBLOCK)) return -EINVAL; ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); @@ -218,7 +218,7 @@ asmlinkage long sys_eventfd2(unsigned int count, int flags) * anon_inode_getfd() will install the fd. */ fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx, - flags & O_CLOEXEC); + flags & (O_CLOEXEC | O_NONBLOCK)); if (fd < 0) kfree(ctx); return fd; diff --git a/include/linux/eventfd.h b/include/linux/eventfd.h index a6c0eaedb1b0..a667637b54e3 100644 --- a/include/linux/eventfd.h +++ b/include/linux/eventfd.h @@ -10,11 +10,12 @@ #ifdef CONFIG_EVENTFD -/* For O_CLOEXEC */ +/* For O_CLOEXEC and O_NONBLOCK */ #include /* Flags for eventfd2. */ #define EFD_CLOEXEC O_CLOEXEC +#define EFD_NONBLOCK O_NONBLOCK struct file *eventfd_fget(int fd); int eventfd_signal(struct file *file, int n); -- cgit From 6b1ef0e60d42f2fdaec26baee8327eb156347b4f Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:39 -0700 Subject: flag parameters: NONBLOCK in timerfd_create This patch adds support for the TFD_NONBLOCK flag to timerfd_create. The additional changes needed are minimal. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_timerfd_create # ifdef __x86_64__ # define __NR_timerfd_create 283 # elif defined __i386__ # define __NR_timerfd_create 322 # else # error "need __NR_timerfd_create" # endif #endif #define TFD_NONBLOCK O_NONBLOCK int main (void) { int fd = syscall (__NR_timerfd_create, CLOCK_REALTIME, 0); if (fd == -1) { puts ("timerfd_create(0) failed"); return 1; } int fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { puts ("timerfd_create(0) set non-blocking mode"); return 1; } close (fd); fd = syscall (__NR_timerfd_create, CLOCK_REALTIME, TFD_NONBLOCK); if (fd == -1) { puts ("timerfd_create(TFD_NONBLOCK) failed"); return 1; } fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { puts ("timerfd_create(TFD_NONBLOCK) set non-blocking mode"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/timerfd.c | 4 ++-- include/linux/timerfd.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/timerfd.c b/fs/timerfd.c index c6ef5e33cb34..75d44efe346c 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -184,7 +184,7 @@ asmlinkage long sys_timerfd_create(int clockid, int flags) int ufd; struct timerfd_ctx *ctx; - if (flags & ~TFD_CLOEXEC) + if (flags & ~(TFD_CLOEXEC | TFD_NONBLOCK)) return -EINVAL; if (clockid != CLOCK_MONOTONIC && clockid != CLOCK_REALTIME) @@ -199,7 +199,7 @@ asmlinkage long sys_timerfd_create(int clockid, int flags) hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, - flags & O_CLOEXEC); + flags & (O_CLOEXEC | O_NONBLOCK)); if (ufd < 0) kfree(ctx); diff --git a/include/linux/timerfd.h b/include/linux/timerfd.h index 96ed97dff00f..86cb0501d3e2 100644 --- a/include/linux/timerfd.h +++ b/include/linux/timerfd.h @@ -8,7 +8,7 @@ #ifndef _LINUX_TIMERFD_H #define _LINUX_TIMERFD_H -/* For O_CLOEXEC */ +/* For O_CLOEXEC and O_NONBLOCK */ #include /* Flags for timerfd_settime. */ @@ -16,6 +16,7 @@ /* Flags for timerfd_create. */ #define TFD_CLOEXEC O_CLOEXEC +#define TFD_NONBLOCK O_NONBLOCK #endif /* _LINUX_TIMERFD_H */ -- cgit From be61a86d7237dd80510615f38ae21d6e1e98660c Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:40 -0700 Subject: flag parameters: NONBLOCK in pipe This patch adds O_NONBLOCK support to pipe2. It is minimally more involved than the patches for eventfd et.al but still trivial. The interfaces of the create_write_pipe and create_read_pipe helper functions were changed and the one other caller as well. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_pipe2 # ifdef __x86_64__ # define __NR_pipe2 293 # elif defined __i386__ # define __NR_pipe2 331 # else # error "need __NR_pipe2" # endif #endif int main (void) { int fds[2]; if (syscall (__NR_pipe2, fds, 0) == -1) { puts ("pipe2(0) failed"); return 1; } for (int i = 0; i < 2; ++i) { int fl = fcntl (fds[i], F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { printf ("pipe2(0) set non-blocking mode for fds[%d]\n", i); return 1; } close (fds[i]); } if (syscall (__NR_pipe2, fds, O_NONBLOCK) == -1) { puts ("pipe2(O_NONBLOCK) failed"); return 1; } for (int i = 0; i < 2; ++i) { int fl = fcntl (fds[i], F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { printf ("pipe2(O_NONBLOCK) does not set non-blocking mode for fds[%d]\n", i); return 1; } close (fds[i]); } puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/pipe.c | 14 +++++++------- include/linux/fs.h | 4 ++-- kernel/kmod.c | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/pipe.c b/fs/pipe.c index 68e82061070c..10c4e9aa5c49 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -950,7 +950,7 @@ fail_inode: return NULL; } -struct file *create_write_pipe(void) +struct file *create_write_pipe(int flags) { int err; struct inode *inode; @@ -983,7 +983,7 @@ struct file *create_write_pipe(void) goto err_dentry; f->f_mapping = inode->i_mapping; - f->f_flags = O_WRONLY; + f->f_flags = O_WRONLY | (flags & O_NONBLOCK); f->f_version = 0; return f; @@ -1007,7 +1007,7 @@ void free_write_pipe(struct file *f) put_filp(f); } -struct file *create_read_pipe(struct file *wrf) +struct file *create_read_pipe(struct file *wrf, int flags) { struct file *f = get_empty_filp(); if (!f) @@ -1019,7 +1019,7 @@ struct file *create_read_pipe(struct file *wrf) f->f_mapping = wrf->f_path.dentry->d_inode->i_mapping; f->f_pos = 0; - f->f_flags = O_RDONLY; + f->f_flags = O_RDONLY | (flags & O_NONBLOCK); f->f_op = &read_pipe_fops; f->f_mode = FMODE_READ; f->f_version = 0; @@ -1033,13 +1033,13 @@ int do_pipe_flags(int *fd, int flags) int error; int fdw, fdr; - if (flags & ~O_CLOEXEC) + if (flags & ~(O_CLOEXEC | O_NONBLOCK)) return -EINVAL; - fw = create_write_pipe(); + fw = create_write_pipe(flags); if (IS_ERR(fw)) return PTR_ERR(fw); - fr = create_read_pipe(fw); + fr = create_read_pipe(fw, flags); error = PTR_ERR(fr); if (IS_ERR(fr)) goto err_write_pipe; diff --git a/include/linux/fs.h b/include/linux/fs.h index 0e80cd717d32..4b86f806014c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1778,8 +1778,8 @@ static inline void allow_write_access(struct file *file) } extern int do_pipe(int *); extern int do_pipe_flags(int *, int); -extern struct file *create_read_pipe(struct file *f); -extern struct file *create_write_pipe(void); +extern struct file *create_read_pipe(struct file *f, int flags); +extern struct file *create_write_pipe(int flags); extern void free_write_pipe(struct file *); extern struct file *do_filp_open(int dfd, const char *pathname, diff --git a/kernel/kmod.c b/kernel/kmod.c index 90d7af1c1655..2989f67c4446 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -417,12 +417,12 @@ int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, { struct file *f; - f = create_write_pipe(); + f = create_write_pipe(0); if (IS_ERR(f)) return PTR_ERR(f); *filp = f; - f = create_read_pipe(f); + f = create_read_pipe(f, 0); if (IS_ERR(f)) { free_write_pipe(*filp); return PTR_ERR(f); -- cgit From 510df2dd482496083e1c3b1a8c9b6afd5fa4c7d7 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:41 -0700 Subject: flag parameters: NONBLOCK in inotify_init This patch adds non-blocking support for inotify_init1. The additional changes needed are minimal. The following test must be adjusted for architectures other than x86 and x86-64 and in case the syscall numbers changed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #ifndef __NR_inotify_init1 # ifdef __x86_64__ # define __NR_inotify_init1 294 # elif defined __i386__ # define __NR_inotify_init1 332 # else # error "need __NR_inotify_init1" # endif #endif #define IN_NONBLOCK O_NONBLOCK int main (void) { int fd = syscall (__NR_inotify_init1, 0); if (fd == -1) { puts ("inotify_init1(0) failed"); return 1; } int fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if (fl & O_NONBLOCK) { puts ("inotify_init1(0) set non-blocking mode"); return 1; } close (fd); fd = syscall (__NR_inotify_init1, IN_NONBLOCK); if (fd == -1) { puts ("inotify_init1(IN_NONBLOCK) failed"); return 1; } fl = fcntl (fd, F_GETFL); if (fl == -1) { puts ("fcntl failed"); return 1; } if ((fl & O_NONBLOCK) == 0) { puts ("inotify_init1(IN_NONBLOCK) set non-blocking mode"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/inotify_user.c | 4 ++-- include/linux/inotify.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/inotify_user.c b/fs/inotify_user.c index 851005998cd4..dc7e1f619748 100644 --- a/fs/inotify_user.c +++ b/fs/inotify_user.c @@ -574,7 +574,7 @@ asmlinkage long sys_inotify_init1(int flags) struct file *filp; int fd, ret; - if (flags & ~IN_CLOEXEC) + if (flags & ~(IN_CLOEXEC | IN_NONBLOCK)) return -EINVAL; fd = get_unused_fd_flags(flags & O_CLOEXEC); @@ -613,7 +613,7 @@ asmlinkage long sys_inotify_init1(int flags) filp->f_path.dentry = dget(inotify_mnt->mnt_root); filp->f_mapping = filp->f_path.dentry->d_inode->i_mapping; filp->f_mode = FMODE_READ; - filp->f_flags = O_RDONLY; + filp->f_flags = O_RDONLY | (flags & O_NONBLOCK); filp->private_data = dev; INIT_LIST_HEAD(&dev->events); diff --git a/include/linux/inotify.h b/include/linux/inotify.h index 72ef82120512..bd578578a8b9 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -7,7 +7,7 @@ #ifndef _LINUX_INOTIFY_H #define _LINUX_INOTIFY_H -/* For O_CLOEXEC */ +/* For O_CLOEXEC and O_NONBLOCK */ #include #include @@ -67,6 +67,7 @@ struct inotify_event { /* Flags for sys_inotify_init1. */ #define IN_CLOEXEC O_CLOEXEC +#define IN_NONBLOCK O_NONBLOCK #ifdef __KERNEL__ -- cgit From 9fe5ad9c8cef9ad5873d8ee55d1cf00d9b607df0 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 23 Jul 2008 21:29:43 -0700 Subject: flag parameters add-on: remove epoll_create size param Remove the size parameter from the new epoll_create syscall and renames the syscall itself. The updated test program follows. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include #include #include #include #ifndef __NR_epoll_create2 # ifdef __x86_64__ # define __NR_epoll_create2 291 # elif defined __i386__ # define __NR_epoll_create2 329 # else # error "need __NR_epoll_create2" # endif #endif #define EPOLL_CLOEXEC O_CLOEXEC int main (void) { int fd = syscall (__NR_epoll_create2, 0); if (fd == -1) { puts ("epoll_create2(0) failed"); return 1; } int coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if (coe & FD_CLOEXEC) { puts ("epoll_create2(0) set close-on-exec flag"); return 1; } close (fd); fd = syscall (__NR_epoll_create2, EPOLL_CLOEXEC); if (fd == -1) { puts ("epoll_create2(EPOLL_CLOEXEC) failed"); return 1; } coe = fcntl (fd, F_GETFD); if (coe == -1) { puts ("fcntl failed"); return 1; } if ((coe & FD_CLOEXEC) == 0) { puts ("epoll_create2(EPOLL_CLOEXEC) set close-on-exec flag"); return 1; } close (fd); puts ("OK"); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Ulrich Drepper Acked-by: Davide Libenzi Cc: Michael Kerrisk Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32entry.S | 2 +- arch/x86/kernel/syscall_table_32.S | 2 +- fs/eventpoll.c | 18 ++++++++++-------- include/asm-x86/unistd_32.h | 2 +- include/asm-x86/unistd_64.h | 4 ++-- include/linux/eventpoll.h | 2 +- include/linux/syscalls.h | 2 +- 7 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 4541073dd837..e4bd1793a5e4 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -828,7 +828,7 @@ ia32_sys_call_table: .quad compat_sys_timerfd_gettime .quad compat_sys_signalfd4 .quad sys_eventfd2 - .quad sys_epoll_create2 + .quad sys_epoll_create1 .quad sys_dup3 /* 330 */ .quad sys_pipe2 .quad sys_inotify_init1 diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index f59aba5ff0f0..d44395ff34c3 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -328,7 +328,7 @@ ENTRY(sys_call_table) .long sys_timerfd_gettime .long sys_signalfd4 .long sys_eventfd2 - .long sys_epoll_create2 + .long sys_epoll_create1 .long sys_dup3 /* 330 */ .long sys_pipe2 .long sys_inotify_init1 diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 2fdad4204044..0c87474f7917 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1046,7 +1046,7 @@ retry: * RB tree. With the current implementation, the "size" parameter is ignored * (besides sanity checks). */ -asmlinkage long sys_epoll_create2(int size, int flags) +asmlinkage long sys_epoll_create1(int flags) { int error, fd = -1; struct eventpoll *ep; @@ -1058,14 +1058,13 @@ asmlinkage long sys_epoll_create2(int size, int flags) return -EINVAL; DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d)\n", - current, size)); + current, flags)); /* - * Sanity check on the size parameter, and create the internal data - * structure ( "struct eventpoll" ). + * Create the internal data structure ( "struct eventpoll" ). */ - error = -EINVAL; - if (size <= 0 || (error = ep_alloc(&ep)) < 0) { + error = ep_alloc(&ep); + if (error < 0) { fd = error; goto error_return; } @@ -1081,14 +1080,17 @@ asmlinkage long sys_epoll_create2(int size, int flags) error_return: DNPRINTK(3, (KERN_INFO "[%p] eventpoll: sys_epoll_create(%d) = %d\n", - current, size, fd)); + current, flags, fd)); return fd; } asmlinkage long sys_epoll_create(int size) { - return sys_epoll_create2(size, 0); + if (size < 0) + return -EINVAL; + + return sys_epoll_create1(0); } /* diff --git a/include/asm-x86/unistd_32.h b/include/asm-x86/unistd_32.h index b3daf503ab93..d7394673b772 100644 --- a/include/asm-x86/unistd_32.h +++ b/include/asm-x86/unistd_32.h @@ -334,7 +334,7 @@ #define __NR_timerfd_gettime 326 #define __NR_signalfd4 327 #define __NR_eventfd2 328 -#define __NR_epoll_create2 329 +#define __NR_epoll_create1 329 #define __NR_dup3 330 #define __NR_pipe2 331 #define __NR_inotify_init1 332 diff --git a/include/asm-x86/unistd_64.h b/include/asm-x86/unistd_64.h index c8cb88d70c6b..3a341d791792 100644 --- a/include/asm-x86/unistd_64.h +++ b/include/asm-x86/unistd_64.h @@ -645,8 +645,8 @@ __SYSCALL(__NR_paccept, sys_paccept) __SYSCALL(__NR_signalfd4, sys_signalfd4) #define __NR_eventfd2 290 __SYSCALL(__NR_eventfd2, sys_eventfd2) -#define __NR_epoll_create2 291 -__SYSCALL(__NR_epoll_create2, sys_epoll_create2) +#define __NR_epoll_create1 291 +__SYSCALL(__NR_epoll_create1, sys_epoll_create1) #define __NR_dup3 292 __SYSCALL(__NR_dup3, sys_dup3) #define __NR_pipe2 293 diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index 1cfaa40059c8..f1e1d3c47125 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -18,7 +18,7 @@ #include #include -/* Flags for epoll_create2. */ +/* Flags for epoll_create1. */ #define EPOLL_CLOEXEC O_CLOEXEC /* Valid opcodes to issue to sys_epoll_ctl() */ diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 93a7e7f017a6..06f2bf76c030 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -431,7 +431,7 @@ asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp); asmlinkage long sys_epoll_create(int size); -asmlinkage long sys_epoll_create2(int size, int flags); +asmlinkage long sys_epoll_create1(int flags); asmlinkage long sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user *event); asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events, -- cgit From 102eb97564c73ea73645b38599c5cbe6f54b030c Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 23 Jul 2008 21:29:55 -0700 Subject: spi: make spi_board_info.modalias a char array Currently, 'modalias' in the spi_device structure is a 'const char *'. The spi_new_device() function fills in the modalias value from a passed in spi_board_info data block. Since it is a pointer copy, the new spi_device remains dependent on the spi_board_info structure after the new spi_device is registered (no other fields in spi_device directly depend on the spi_board_info structure; all of the other data is copied). This causes a problem when dynamically propulating the list of attached SPI devices. For example, in arch/powerpc, the list of SPI devices can be populated from data in the device tree. With the current code, the device tree adapter must kmalloc() a new spi_board_info structure for each new SPI device it finds in the device tree, and there is no simple mechanism in place for keeping track of these allocations. This patch changes modalias from a 'const char *' to a fixed char array. By copying the modalias string instead of referencing it, the dependency on the spi_board_info structure is eliminated and an outside caller does not need to maintain a separate spi_board_info allocation for each device. If searched through the code to the best of my ability for any references to modalias which may be affected by this change and haven't found anything. It has been tested with the lite5200b platform in arch/powerpc. [dbrownell@users.sourceforge.net: cope with linux-next changes: KOBJ_NAME_LEN obliterated, etc] Signed-off-by: Grant Likely Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi.c | 4 +++- include/linux/spi/spi.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1771b2456bfa..ecca4a6a6f94 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -218,6 +218,8 @@ struct spi_device *spi_new_device(struct spi_master *master, if (!spi_master_get(master)) return NULL; + WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); + proxy = kzalloc(sizeof *proxy, GFP_KERNEL); if (!proxy) { dev_err(dev, "can't alloc dev for cs%d\n", @@ -229,7 +231,7 @@ struct spi_device *spi_new_device(struct spi_master *master, proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; proxy->irq = chip->irq; - proxy->modalias = chip->modalias; + strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id, "%s.%u", master->dev.bus_id, diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index b9a76c972084..a9cc29d46653 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -82,7 +82,7 @@ struct spi_device { int irq; void *controller_state; void *controller_data; - const char *modalias; + char modalias[32]; /* * likely need more hooks for more protocol options affecting how -- cgit From aa55ddf340c9fa3f303ee16bbf35887e42c50304 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Wed, 23 Jul 2008 21:30:29 -0700 Subject: autofs4: remove unused ioctls The ioctls AUTOFS_IOC_TOGGLEREGHOST and AUTOFS_IOC_ASKREGHOST were added several years ago but what they were intended for has never been implemented (as far as I'm aware noone uses them) so remove them. Signed-off-by: Ian Kent Reviewed-by: Jeff Moyer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/autofs4/root.c | 68 +----------------------------------------------- fs/compat_ioctl.c | 2 -- include/linux/auto_fs4.h | 2 -- 3 files changed, 1 insertion(+), 71 deletions(-) (limited to 'include/linux') diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index ae22bde0bbd7..bcfb2dc0a61b 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -25,7 +25,6 @@ static int autofs4_dir_rmdir(struct inode *,struct dentry *); static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); static int autofs4_dir_open(struct inode *inode, struct file *file); -static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); static void *autofs4_follow_link(struct dentry *, struct nameidata *); @@ -36,7 +35,7 @@ const struct file_operations autofs4_root_operations = { .open = dcache_dir_open, .release = dcache_dir_close, .read = generic_read_dir, - .readdir = autofs4_root_readdir, + .readdir = dcache_readdir, .ioctl = autofs4_root_ioctl, }; @@ -71,28 +70,6 @@ const struct inode_operations autofs4_dir_inode_operations = { .rmdir = autofs4_dir_rmdir, }; -static int autofs4_root_readdir(struct file *file, void *dirent, - filldir_t filldir) -{ - struct autofs_sb_info *sbi = autofs4_sbi(file->f_path.dentry->d_sb); - int oz_mode = autofs4_oz_mode(sbi); - - DPRINTK("called, filp->f_pos = %lld", file->f_pos); - - /* - * Don't set reghost flag if: - * 1) f_pos is larger than zero -- we've already been here. - * 2) we haven't even enabled reghosting in the 1st place. - * 3) this is the daemon doing a readdir - */ - if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled) - sbi->needs_reghost = 1; - - DPRINTK("needs_reghost = %d", sbi->needs_reghost); - - return dcache_readdir(file, dirent, filldir); -} - static int autofs4_dir_open(struct inode *inode, struct file *file) { struct dentry *dentry = file->f_path.dentry; @@ -858,44 +835,6 @@ static inline int autofs4_get_protosubver(struct autofs_sb_info *sbi, int __user return put_user(sbi->sub_version, p); } -/* - * Tells the daemon whether we need to reghost or not. Also, clears - * the reghost_needed flag. - */ -static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p) -{ - int status; - - DPRINTK("returning %d", sbi->needs_reghost); - - status = put_user(sbi->needs_reghost, p); - if (status) - return status; - - sbi->needs_reghost = 0; - return 0; -} - -/* - * Enable / Disable reghosting ioctl() operation - */ -static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p) -{ - int status; - int val; - - status = get_user(val, p); - - DPRINTK("reghost = %d", val); - - if (status) - return status; - - /* turn on/off reghosting, with the val */ - sbi->reghost_enabled = val; - return 0; -} - /* * Tells the daemon whether it can umount the autofs mount. */ @@ -960,11 +899,6 @@ static int autofs4_root_ioctl(struct inode *inode, struct file *filp, case AUTOFS_IOC_SETTIMEOUT: return autofs4_get_set_timeout(sbi, p); - case AUTOFS_IOC_TOGGLEREGHOST: - return autofs4_toggle_reghost(sbi, p); - case AUTOFS_IOC_ASKREGHOST: - return autofs4_ask_reghost(sbi, p); - case AUTOFS_IOC_ASKUMOUNT: return autofs4_ask_umount(filp->f_path.mnt, p); diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 7b3a03c7c6a9..18e2c548161d 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -2297,8 +2297,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) -COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) -COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) /* Raw devices */ COMPATIBLE_IOCTL(RAW_SETBIND) diff --git a/include/linux/auto_fs4.h b/include/linux/auto_fs4.h index 31a29541b504..b785c6f8644d 100644 --- a/include/linux/auto_fs4.h +++ b/include/linux/auto_fs4.h @@ -98,8 +98,6 @@ union autofs_v5_packet_union { #define AUTOFS_IOC_EXPIRE_INDIRECT AUTOFS_IOC_EXPIRE_MULTI #define AUTOFS_IOC_EXPIRE_DIRECT AUTOFS_IOC_EXPIRE_MULTI #define AUTOFS_IOC_PROTOSUBVER _IOR(0x93,0x67,int) -#define AUTOFS_IOC_ASKREGHOST _IOR(0x93,0x68,int) -#define AUTOFS_IOC_TOGGLEREGHOST _IOR(0x93,0x69,int) #define AUTOFS_IOC_ASKUMOUNT _IOR(0x93,0x70,int) -- cgit From 5ad31a575157147b43fa84ef1e21471661653878 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 23 Jul 2008 21:30:33 -0700 Subject: rtc: remove BKL for ioctl() Remove implicit use of BKL in ioctl() from the RTC framework. Instead, the rtc->ops_lock is used. That's the same lock that already protects the RTC operations when they're issued through the exported rtc_*() calls in drivers/rtc/interface.c ... making this a bugfix, not just a cleanup, since both ioctl calls and set_alarm() need to update IRQ enable flags and that implies a common lock (which RTC drivers as a rule do not provide on their own). A new comment at the declaration of "struct rtc_class_ops" summarizes current locking rules. It's not clear to me that the exceptions listed there should exist ... if not, those are pre-existing problems which can be fixed in a patch that doesn't relate to BKL removal. Signed-off-by: David Brownell Cc: Alan Cox Cc: Jonathan Corbet Acked-by: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-dev.c | 58 +++++++++++++++++++++++++++++++++------------------ include/linux/rtc.h | 17 +++++++++++++++ 2 files changed, 55 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 0114a78b7cbb..0a870b7e5c32 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -209,7 +209,7 @@ static unsigned int rtc_dev_poll(struct file *file, poll_table *wait) return (data != 0) ? (POLLIN | POLLRDNORM) : 0; } -static int rtc_dev_ioctl(struct inode *inode, struct file *file, +static long rtc_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int err = 0; @@ -219,6 +219,10 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, struct rtc_wkalrm alarm; void __user *uarg = (void __user *) arg; + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return -EBUSY; + /* check that the calling task has appropriate permissions * for certain ioctls. doing this check here is useful * to avoid duplicate code in each driver. @@ -227,26 +231,31 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, case RTC_EPOCH_SET: case RTC_SET_TIME: if (!capable(CAP_SYS_TIME)) - return -EACCES; + err = -EACCES; break; case RTC_IRQP_SET: if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) - return -EACCES; + err = -EACCES; break; case RTC_PIE_ON: if (rtc->irq_freq > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) - return -EACCES; + err = -EACCES; break; } + if (err) + goto done; + /* try the driver's ioctl interface */ if (ops->ioctl) { err = ops->ioctl(rtc->dev.parent, cmd, arg); - if (err != -ENOIOCTLCMD) + if (err != -ENOIOCTLCMD) { + mutex_unlock(&rtc->ops_lock); return err; + } } /* if the driver does not provide the ioctl interface @@ -265,15 +274,19 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, switch (cmd) { case RTC_ALM_READ: + mutex_unlock(&rtc->ops_lock); + err = rtc_read_alarm(rtc, &alarm); if (err < 0) return err; if (copy_to_user(uarg, &alarm.time, sizeof(tm))) - return -EFAULT; - break; + err = -EFAULT; + return err; case RTC_ALM_SET: + mutex_unlock(&rtc->ops_lock); + if (copy_from_user(&alarm.time, uarg, sizeof(tm))) return -EFAULT; @@ -321,24 +334,26 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, } } - err = rtc_set_alarm(rtc, &alarm); - break; + return rtc_set_alarm(rtc, &alarm); case RTC_RD_TIME: + mutex_unlock(&rtc->ops_lock); + err = rtc_read_time(rtc, &tm); if (err < 0) return err; if (copy_to_user(uarg, &tm, sizeof(tm))) - return -EFAULT; - break; + err = -EFAULT; + return err; case RTC_SET_TIME: + mutex_unlock(&rtc->ops_lock); + if (copy_from_user(&tm, uarg, sizeof(tm))) return -EFAULT; - err = rtc_set_time(rtc, &tm); - break; + return rtc_set_time(rtc, &tm); case RTC_PIE_ON: err = rtc_irq_set_state(rtc, NULL, 1); @@ -376,34 +391,37 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, break; #endif case RTC_WKALM_SET: + mutex_unlock(&rtc->ops_lock); if (copy_from_user(&alarm, uarg, sizeof(alarm))) return -EFAULT; - err = rtc_set_alarm(rtc, &alarm); - break; + return rtc_set_alarm(rtc, &alarm); case RTC_WKALM_RD: + mutex_unlock(&rtc->ops_lock); err = rtc_read_alarm(rtc, &alarm); if (err < 0) return err; if (copy_to_user(uarg, &alarm, sizeof(alarm))) - return -EFAULT; - break; + err = -EFAULT; + return err; #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL case RTC_UIE_OFF: clear_uie(rtc); - return 0; + break; case RTC_UIE_ON: - return set_uie(rtc); + err = set_uie(rtc); #endif default: err = -ENOTTY; break; } +done: + mutex_unlock(&rtc->ops_lock); return err; } @@ -432,7 +450,7 @@ static const struct file_operations rtc_dev_fops = { .llseek = no_llseek, .read = rtc_dev_read, .poll = rtc_dev_poll, - .ioctl = rtc_dev_ioctl, + .unlocked_ioctl = rtc_dev_ioctl, .open = rtc_dev_open, .release = rtc_dev_release, .fasync = rtc_dev_fasync, diff --git a/include/linux/rtc.h b/include/linux/rtc.h index f2d0d1527721..b01fe004cb5e 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -115,6 +115,23 @@ extern void rtc_time_to_tm(unsigned long time, struct rtc_time *tm); extern struct class *rtc_class; +/* + * For these RTC methods the device parameter is the physical device + * on whatever bus holds the hardware (I2C, Platform, SPI, etc), which + * was passed to rtc_device_register(). Its driver_data normally holds + * device state, including the rtc_device pointer for the RTC. + * + * Most of these methods are called with rtc_device.ops_lock held, + * through the rtc_*(struct rtc_device *, ...) calls. + * + * The (current) exceptions are mostly filesystem hooks: + * - the proc() hook for procfs + * - non-ioctl() chardev hooks: open(), release(), read_callback() + * - periodic irq calls: irq_set_state(), irq_set_freq() + * + * REVISIT those periodic irq calls *do* have ops_lock when they're + * issued through ioctl() ... + */ struct rtc_class_ops { int (*open)(struct device *); void (*release)(struct device *); -- cgit From 53e84b672c1a8190af2b376c35c7a39cf1214f59 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 23 Jul 2008 21:30:36 -0700 Subject: rtc: ds1305/ds1306 driver Support the Dallas/Maxim DS1305 and DS1306 RTC chips. These use SPI, and support alarms, NVRAM, and a trickle charger for use when their backup power supply is a supercap or rechargeable cell. This basic driver doesn't yet support suspend/resume or wakealarms. Signed-off-by: David Brownell Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-ds1305.c | 847 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/ds1305.h | 35 ++ 4 files changed, 893 insertions(+) create mode 100644 drivers/rtc/rtc-ds1305.c create mode 100644 include/linux/spi/ds1305.h (limited to 'include/linux') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index beffb834c440..90ab73825401 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -282,6 +282,16 @@ config RTC_DRV_M41T94 This driver can also be built as a module. If so, the module will be called rtc-m41t94. +config RTC_DRV_DS1305 + tristate "Dallas/Maxim DS1305/DS1306" + help + Select this driver to get support for the Dallas/Maxim DS1305 + and DS1306 real time clock chips. These support a trickle + charger, alarms, and NVRAM in addition to the clock. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1305. + config RTC_DRV_MAX6902 tristate "Maxim MAX6902" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index b0e1af54f800..18622ef84cab 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o +obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c new file mode 100644 index 000000000000..b91d02a3ace9 --- /dev/null +++ b/drivers/rtc/rtc-ds1305.c @@ -0,0 +1,847 @@ +/* + * rtc-ds1305.c -- driver for DS1305 and DS1306 SPI RTC chips + * + * Copyright (C) 2008 David Brownell + * + * 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 +#include +#include +#include +#include + +#include +#include + + +/* + * Registers ... mask DS1305_WRITE into register address to write, + * otherwise you're reading it. All non-bitmask values are BCD. + */ +#define DS1305_WRITE 0x80 + + +/* RTC date/time ... the main special cases are that we: + * - Need fancy "hours" encoding in 12hour mode + * - Don't rely on the "day-of-week" field (or tm_wday) + * - Are a 21st-century clock (2000 <= year < 2100) + */ +#define DS1305_RTC_LEN 7 /* bytes for RTC regs */ + +#define DS1305_SEC 0x00 /* register addresses */ +#define DS1305_MIN 0x01 +#define DS1305_HOUR 0x02 +# define DS1305_HR_12 0x40 /* set == 12 hr mode */ +# define DS1305_HR_PM 0x20 /* set == PM (12hr mode) */ +#define DS1305_WDAY 0x03 +#define DS1305_MDAY 0x04 +#define DS1305_MON 0x05 +#define DS1305_YEAR 0x06 + + +/* The two alarms have only sec/min/hour/wday fields (ALM_LEN). + * DS1305_ALM_DISABLE disables a match field (some combos are bad). + * + * NOTE that since we don't use WDAY, we limit ourselves to alarms + * only one day into the future (vs potentially up to a week). + * + * NOTE ALSO that while we could generate once-a-second IRQs (UIE), we + * don't currently support them. We'd either need to do it only when + * no alarm is pending (not the standard model), or to use the second + * alarm (implying that this is a DS1305 not DS1306, *and* that either + * it's wired up a second IRQ we know, or that INTCN is set) + */ +#define DS1305_ALM_LEN 4 /* bytes for ALM regs */ +#define DS1305_ALM_DISABLE 0x80 + +#define DS1305_ALM0(r) (0x07 + (r)) /* register addresses */ +#define DS1305_ALM1(r) (0x0b + (r)) + + +/* three control registers */ +#define DS1305_CONTROL_LEN 3 /* bytes of control regs */ + +#define DS1305_CONTROL 0x0f /* register addresses */ +# define DS1305_nEOSC 0x80 /* low enables oscillator */ +# define DS1305_WP 0x40 /* write protect */ +# define DS1305_INTCN 0x04 /* clear == only int0 used */ +# define DS1306_1HZ 0x04 /* enable 1Hz output */ +# define DS1305_AEI1 0x02 /* enable ALM1 IRQ */ +# define DS1305_AEI0 0x01 /* enable ALM0 IRQ */ +#define DS1305_STATUS 0x10 +/* status has just AEIx bits, mirrored as IRQFx */ +#define DS1305_TRICKLE 0x11 +/* trickle bits are defined in */ + +/* a bunch of NVRAM */ +#define DS1305_NVRAM_LEN 96 /* bytes of NVRAM */ + +#define DS1305_NVRAM 0x20 /* register addresses */ + + +struct ds1305 { + struct spi_device *spi; + struct rtc_device *rtc; + + struct work_struct work; + + unsigned long flags; +#define FLAG_EXITING 0 + + bool hr12; + u8 ctrl[DS1305_CONTROL_LEN]; +}; + + +/*----------------------------------------------------------------------*/ + +/* + * Utilities ... tolerate 12-hour AM/PM notation in case of non-Linux + * software (like a bootloader) which may require it. + */ + +static unsigned bcd2hour(u8 bcd) +{ + if (bcd & DS1305_HR_12) { + unsigned hour = 0; + + bcd &= ~DS1305_HR_12; + if (bcd & DS1305_HR_PM) { + hour = 12; + bcd &= ~DS1305_HR_PM; + } + hour += BCD2BIN(bcd); + return hour - 1; + } + return BCD2BIN(bcd); +} + +static u8 hour2bcd(bool hr12, int hour) +{ + if (hr12) { + hour++; + if (hour <= 12) + return DS1305_HR_12 | BIN2BCD(hour); + hour -= 12; + return DS1305_HR_12 | DS1305_HR_PM | BIN2BCD(hour); + } + return BIN2BCD(hour); +} + +/*----------------------------------------------------------------------*/ + +/* + * Interface to RTC framework + */ + +#ifdef CONFIG_RTC_INTF_DEV + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_ioctl(struct device *dev, unsigned cmd, unsigned long arg) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 buf[2]; + int status = -ENOIOCTLCMD; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + + switch (cmd) { + case RTC_AIE_OFF: + status = 0; + if (!(buf[1] & DS1305_AEI0)) + goto done; + buf[1] &= ~DS1305_AEI0; + break; + + case RTC_AIE_ON: + status = 0; + if (ds1305->ctrl[0] & DS1305_AEI0) + goto done; + buf[1] |= DS1305_AEI0; + break; + } + if (status == 0) { + status = spi_write_then_read(ds1305->spi, buf, sizeof buf, + NULL, 0); + if (status >= 0) + ds1305->ctrl[0] = buf[1]; + } + +done: + return status; +} + +#else +#define ds1305_ioctl NULL +#endif + +/* + * Get/set of date and time is pretty normal. + */ + +static int ds1305_get_time(struct device *dev, struct rtc_time *time) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 addr = DS1305_SEC; + u8 buf[DS1305_RTC_LEN]; + int status; + + /* Use write-then-read to get all the date/time registers + * since dma from stack is nonportable + */ + status = spi_write_then_read(ds1305->spi, &addr, sizeof addr, + buf, sizeof buf); + if (status < 0) + return status; + + dev_vdbg(dev, "%s: %02x %02x %02x, %02x %02x %02x %02x\n", + "read", buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6]); + + /* Decode the registers */ + time->tm_sec = BCD2BIN(buf[DS1305_SEC]); + time->tm_min = BCD2BIN(buf[DS1305_MIN]); + time->tm_hour = bcd2hour(buf[DS1305_HOUR]); + time->tm_wday = buf[DS1305_WDAY] - 1; + time->tm_mday = BCD2BIN(buf[DS1305_MDAY]); + time->tm_mon = BCD2BIN(buf[DS1305_MON]) - 1; + time->tm_year = BCD2BIN(buf[DS1305_YEAR]) + 100; + + dev_vdbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", time->tm_sec, time->tm_min, + time->tm_hour, time->tm_mday, + time->tm_mon, time->tm_year, time->tm_wday); + + /* Time may not be set */ + return rtc_valid_tm(time); +} + +static int ds1305_set_time(struct device *dev, struct rtc_time *time) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 buf[1 + DS1305_RTC_LEN]; + u8 *bp = buf; + + dev_vdbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", time->tm_sec, time->tm_min, + time->tm_hour, time->tm_mday, + time->tm_mon, time->tm_year, time->tm_wday); + + /* Write registers starting at the first time/date address. */ + *bp++ = DS1305_WRITE | DS1305_SEC; + + *bp++ = BIN2BCD(time->tm_sec); + *bp++ = BIN2BCD(time->tm_min); + *bp++ = hour2bcd(ds1305->hr12, time->tm_hour); + *bp++ = (time->tm_wday < 7) ? (time->tm_wday + 1) : 1; + *bp++ = BIN2BCD(time->tm_mday); + *bp++ = BIN2BCD(time->tm_mon + 1); + *bp++ = BIN2BCD(time->tm_year - 100); + + dev_dbg(dev, "%s: %02x %02x %02x, %02x %02x %02x %02x\n", + "write", buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + /* use write-then-read since dma from stack is nonportable */ + return spi_write_then_read(ds1305->spi, buf, sizeof buf, + NULL, 0); +} + +/* + * Get/set of alarm is a bit funky: + * + * - First there's the inherent raciness of getting the (partitioned) + * status of an alarm that could trigger while we're reading parts + * of that status. + * + * - Second there's its limited range (we could increase it a bit by + * relying on WDAY), which means it will easily roll over. + * + * - Third there's the choice of two alarms and alarm signals. + * Here we use ALM0 and expect that nINT0 (open drain) is used; + * that's the only real option for DS1306 runtime alarms, and is + * natural on DS1305. + * + * - Fourth, there's also ALM1, and a second interrupt signal: + * + On DS1305 ALM1 uses nINT1 (when INTCN=1) else nINT0; + * + On DS1306 ALM1 only uses INT1 (an active high pulse) + * and it won't work when VCC1 is active. + * + * So to be most general, we should probably set both alarms to the + * same value, letting ALM1 be the wakeup event source on DS1306 + * and handling several wiring options on DS1305. + * + * - Fifth, we support the polled mode (as well as possible; why not?) + * even when no interrupt line is wired to an IRQ. + */ + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_get_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + struct spi_device *spi = ds1305->spi; + u8 addr; + int status; + u8 buf[DS1305_ALM_LEN]; + + /* Refresh control register cache BEFORE reading ALM0 registers, + * since reading alarm registers acks any pending IRQ. That + * makes returning "pending" status a bit of a lie, but that bit + * of EFI status is at best fragile anyway (given IRQ handlers). + */ + addr = DS1305_CONTROL; + status = spi_write_then_read(spi, &addr, sizeof addr, + ds1305->ctrl, sizeof ds1305->ctrl); + if (status < 0) + return status; + + alm->enabled = !!(ds1305->ctrl[0] & DS1305_AEI0); + alm->pending = !!(ds1305->ctrl[1] & DS1305_AEI0); + + /* get and check ALM0 registers */ + addr = DS1305_ALM0(DS1305_SEC); + status = spi_write_then_read(spi, &addr, sizeof addr, + buf, sizeof buf); + if (status < 0) + return status; + + dev_vdbg(dev, "%s: %02x %02x %02x %02x\n", + "alm0 read", buf[DS1305_SEC], buf[DS1305_MIN], + buf[DS1305_HOUR], buf[DS1305_WDAY]); + + if ((DS1305_ALM_DISABLE & buf[DS1305_SEC]) + || (DS1305_ALM_DISABLE & buf[DS1305_MIN]) + || (DS1305_ALM_DISABLE & buf[DS1305_HOUR])) + return -EIO; + + /* Stuff these values into alm->time and let RTC framework code + * fill in the rest ... and also handle rollover to tomorrow when + * that's needed. + */ + alm->time.tm_sec = BCD2BIN(buf[DS1305_SEC]); + alm->time.tm_min = BCD2BIN(buf[DS1305_MIN]); + alm->time.tm_hour = bcd2hour(buf[DS1305_HOUR]); + alm->time.tm_mday = -1; + alm->time.tm_mon = -1; + alm->time.tm_year = -1; + /* next three fields are unused by Linux */ + alm->time.tm_wday = -1; + alm->time.tm_mday = -1; + alm->time.tm_isdst = -1; + + return 0; +} + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + struct spi_device *spi = ds1305->spi; + unsigned long now, later; + struct rtc_time tm; + int status; + u8 buf[1 + DS1305_ALM_LEN]; + + /* convert desired alarm to time_t */ + status = rtc_tm_to_time(&alm->time, &later); + if (status < 0) + return status; + + /* Read current time as time_t */ + status = ds1305_get_time(dev, &tm); + if (status < 0) + return status; + status = rtc_tm_to_time(&tm, &now); + if (status < 0) + return status; + + /* make sure alarm fires within the next 24 hours */ + if (later <= now) + return -EINVAL; + if ((later - now) > 24 * 60 * 60) + return -EDOM; + + /* disable alarm if needed */ + if (ds1305->ctrl[0] & DS1305_AEI0) { + ds1305->ctrl[0] &= ~DS1305_AEI0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0); + if (status < 0) + return status; + } + + /* write alarm */ + buf[0] = DS1305_WRITE | DS1305_ALM0(DS1305_SEC); + buf[1 + DS1305_SEC] = BIN2BCD(alm->time.tm_sec); + buf[1 + DS1305_MIN] = BIN2BCD(alm->time.tm_min); + buf[1 + DS1305_HOUR] = hour2bcd(ds1305->hr12, alm->time.tm_hour); + buf[1 + DS1305_WDAY] = DS1305_ALM_DISABLE; + + dev_dbg(dev, "%s: %02x %02x %02x %02x\n", + "alm0 write", buf[1 + DS1305_SEC], buf[1 + DS1305_MIN], + buf[1 + DS1305_HOUR], buf[1 + DS1305_WDAY]); + + status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + if (status < 0) + return status; + + /* enable alarm if requested */ + if (alm->enabled) { + ds1305->ctrl[0] |= DS1305_AEI0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0); + } + + return status; +} + +#ifdef CONFIG_PROC_FS + +static int ds1305_proc(struct device *dev, struct seq_file *seq) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + char *diodes = "no"; + char *resistors = ""; + + /* ctrl[2] is treated as read-only; no locking needed */ + if ((ds1305->ctrl[2] & 0xf0) == DS1305_TRICKLE_MAGIC) { + switch (ds1305->ctrl[2] & 0x0c) { + case DS1305_TRICKLE_DS2: + diodes = "2 diodes, "; + break; + case DS1305_TRICKLE_DS1: + diodes = "1 diode, "; + break; + default: + goto done; + } + switch (ds1305->ctrl[2] & 0x03) { + case DS1305_TRICKLE_2K: + resistors = "2k Ohm"; + break; + case DS1305_TRICKLE_4K: + resistors = "4k Ohm"; + break; + case DS1305_TRICKLE_8K: + resistors = "8k Ohm"; + break; + default: + diodes = "no"; + break; + } + } + +done: + return seq_printf(seq, + "trickle_charge\t: %s%s\n", + diodes, resistors); +} + +#else +#define ds1305_proc NULL +#endif + +static const struct rtc_class_ops ds1305_ops = { + .ioctl = ds1305_ioctl, + .read_time = ds1305_get_time, + .set_time = ds1305_set_time, + .read_alarm = ds1305_get_alarm, + .set_alarm = ds1305_set_alarm, + .proc = ds1305_proc, +}; + +static void ds1305_work(struct work_struct *work) +{ + struct ds1305 *ds1305 = container_of(work, struct ds1305, work); + struct mutex *lock = &ds1305->rtc->ops_lock; + struct spi_device *spi = ds1305->spi; + u8 buf[3]; + int status; + + /* lock to protect ds1305->ctrl */ + mutex_lock(lock); + + /* Disable the IRQ, and clear its status ... for now, we "know" + * that if more than one alarm is active, they're in sync. + * Note that reading ALM data registers also clears IRQ status. + */ + ds1305->ctrl[0] &= ~(DS1305_AEI1 | DS1305_AEI0); + ds1305->ctrl[1] = 0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + buf[2] = 0; + + status = spi_write_then_read(spi, buf, sizeof buf, + NULL, 0); + if (status < 0) + dev_dbg(&spi->dev, "clear irq --> %d\n", status); + + mutex_unlock(lock); + + if (!test_bit(FLAG_EXITING, &ds1305->flags)) + enable_irq(spi->irq); + + /* rtc_update_irq() requires an IRQ-disabled context */ + local_irq_disable(); + rtc_update_irq(ds1305->rtc, 1, RTC_AF | RTC_IRQF); + local_irq_enable(); +} + +/* + * This "real" IRQ handler hands off to a workqueue mostly to allow + * mutex locking for ds1305->ctrl ... unlike I2C, we could issue async + * I/O requests in IRQ context (to clear the IRQ status). + */ +static irqreturn_t ds1305_irq(int irq, void *p) +{ + struct ds1305 *ds1305 = p; + + disable_irq(irq); + schedule_work(&ds1305->work); + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +/* + * Interface for NVRAM + */ + +static void msg_init(struct spi_message *m, struct spi_transfer *x, + u8 *addr, size_t count, char *tx, char *rx) +{ + spi_message_init(m); + memset(x, 0, 2 * sizeof(*x)); + + x->tx_buf = addr; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + + x->tx_buf = tx; + x->rx_buf = rx; + x->len = count; + spi_message_add_tail(x, m); +} + +static ssize_t +ds1305_nvram_read(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct spi_device *spi; + u8 addr; + struct spi_message m; + struct spi_transfer x[2]; + int status; + + spi = container_of(kobj, struct spi_device, dev.kobj); + + if (unlikely(off >= DS1305_NVRAM_LEN)) + return 0; + if (count >= DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN; + if ((off + count) > DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN - off; + if (unlikely(!count)) + return count; + + addr = DS1305_NVRAM + off; + msg_init(&m, x, &addr, count, NULL, buf); + + status = spi_sync(spi, &m); + if (status < 0) + dev_err(&spi->dev, "nvram %s error %d\n", "read", status); + return (status < 0) ? status : count; +} + +static ssize_t +ds1305_nvram_write(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct spi_device *spi; + u8 addr; + struct spi_message m; + struct spi_transfer x[2]; + int status; + + spi = container_of(kobj, struct spi_device, dev.kobj); + + if (unlikely(off >= DS1305_NVRAM_LEN)) + return -EFBIG; + if (count >= DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN; + if ((off + count) > DS1305_NVRAM_LEN) + count = DS1305_NVRAM_LEN - off; + if (unlikely(!count)) + return count; + + addr = (DS1305_WRITE | DS1305_NVRAM) + off; + msg_init(&m, x, &addr, count, buf, NULL); + + status = spi_sync(spi, &m); + if (status < 0) + dev_err(&spi->dev, "nvram %s error %d\n", "write", status); + return (status < 0) ? status : count; +} + +static struct bin_attribute nvram = { + .attr.name = "nvram", + .attr.mode = S_IRUGO | S_IWUSR, + .attr.owner = THIS_MODULE, + .read = ds1305_nvram_read, + .write = ds1305_nvram_write, + .size = DS1305_NVRAM_LEN, +}; + +/*----------------------------------------------------------------------*/ + +/* + * Interface to SPI stack + */ + +static int __devinit ds1305_probe(struct spi_device *spi) +{ + struct ds1305 *ds1305; + struct rtc_device *rtc; + int status; + u8 addr, value; + struct ds1305_platform_data *pdata = spi->dev.platform_data; + bool write_ctrl = false; + + /* Sanity check board setup data. This may be hooked up + * in 3wire mode, but we don't care. Note that unless + * there's an inverter in place, this needs SPI_CS_HIGH! + */ + if ((spi->bits_per_word && spi->bits_per_word != 8) + || (spi->max_speed_hz > 2000000) + || !(spi->mode & SPI_CPHA)) + return -EINVAL; + + /* set up driver data */ + ds1305 = kzalloc(sizeof *ds1305, GFP_KERNEL); + if (!ds1305) + return -ENOMEM; + ds1305->spi = spi; + spi_set_drvdata(spi, ds1305); + + /* read and cache control registers */ + addr = DS1305_CONTROL; + status = spi_write_then_read(spi, &addr, sizeof addr, + ds1305->ctrl, sizeof ds1305->ctrl); + if (status < 0) { + dev_dbg(&spi->dev, "can't %s, %d\n", + "read", status); + goto fail0; + } + + dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n", + "read", ds1305->ctrl[0], + ds1305->ctrl[1], ds1305->ctrl[2]); + + /* Sanity check register values ... partially compensating for the + * fact that SPI has no device handshake. A pullup on MISO would + * make these tests fail; but not all systems will have one. If + * some register is neither 0x00 nor 0xff, a chip is likely there. + */ + if ((ds1305->ctrl[0] & 0x38) != 0 || (ds1305->ctrl[1] & 0xfc) != 0) { + dev_dbg(&spi->dev, "RTC chip is not present\n"); + status = -ENODEV; + goto fail0; + } + if (ds1305->ctrl[2] == 0) + dev_dbg(&spi->dev, "chip may not be present\n"); + + /* enable writes if needed ... if we were paranoid it would + * make sense to enable them only when absolutely necessary. + */ + if (ds1305->ctrl[0] & DS1305_WP) { + u8 buf[2]; + + ds1305->ctrl[0] &= ~DS1305_WP; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + + dev_dbg(&spi->dev, "clear WP --> %d\n", status); + if (status < 0) + goto fail0; + } + + /* on DS1305, maybe start oscillator; like most low power + * oscillators, it may take a second to stabilize + */ + if (ds1305->ctrl[0] & DS1305_nEOSC) { + ds1305->ctrl[0] &= ~DS1305_nEOSC; + write_ctrl = true; + dev_warn(&spi->dev, "SET TIME!\n"); + } + + /* ack any pending IRQs */ + if (ds1305->ctrl[1]) { + ds1305->ctrl[1] = 0; + write_ctrl = true; + } + + /* this may need one-time (re)init */ + if (pdata) { + /* maybe enable trickle charge */ + if (((ds1305->ctrl[2] & 0xf0) != DS1305_TRICKLE_MAGIC)) { + ds1305->ctrl[2] = DS1305_TRICKLE_MAGIC + | pdata->trickle; + write_ctrl = true; + } + + /* on DS1306, configure 1 Hz signal */ + if (pdata->is_ds1306) { + if (pdata->en_1hz) { + if (!(ds1305->ctrl[0] & DS1306_1HZ)) { + ds1305->ctrl[0] |= DS1306_1HZ; + write_ctrl = true; + } + } else { + if (ds1305->ctrl[0] & DS1306_1HZ) { + ds1305->ctrl[0] &= ~DS1306_1HZ; + write_ctrl = true; + } + } + } + } + + if (write_ctrl) { + u8 buf[4]; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + buf[2] = ds1305->ctrl[1]; + buf[3] = ds1305->ctrl[2]; + status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + if (status < 0) { + dev_dbg(&spi->dev, "can't %s, %d\n", + "write", status); + goto fail0; + } + + dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n", + "write", ds1305->ctrl[0], + ds1305->ctrl[1], ds1305->ctrl[2]); + } + + /* see if non-Linux software set up AM/PM mode */ + addr = DS1305_HOUR; + status = spi_write_then_read(spi, &addr, sizeof addr, + &value, sizeof value); + if (status < 0) { + dev_dbg(&spi->dev, "read HOUR --> %d\n", status); + goto fail0; + } + + ds1305->hr12 = (DS1305_HR_12 & value) != 0; + if (ds1305->hr12) + dev_dbg(&spi->dev, "AM/PM\n"); + + /* register RTC ... from here on, ds1305->ctrl needs locking */ + rtc = rtc_device_register("ds1305", &spi->dev, + &ds1305_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + status = PTR_ERR(rtc); + dev_dbg(&spi->dev, "register rtc --> %d\n", status); + goto fail0; + } + ds1305->rtc = rtc; + + /* Maybe set up alarm IRQ; be ready to handle it triggering right + * away. NOTE that we don't share this. The signal is active low, + * and we can't ack it before a SPI message delay. We temporarily + * disable the IRQ until it's acked, which lets us work with more + * IRQ trigger modes (not all IRQ controllers can do falling edge). + */ + if (spi->irq) { + INIT_WORK(&ds1305->work, ds1305_work); + status = request_irq(spi->irq, ds1305_irq, + 0, dev_name(&rtc->dev), ds1305); + if (status < 0) { + dev_dbg(&spi->dev, "request_irq %d --> %d\n", + spi->irq, status); + goto fail1; + } + } + + /* export NVRAM */ + status = sysfs_create_bin_file(&spi->dev.kobj, &nvram); + if (status < 0) { + dev_dbg(&spi->dev, "register nvram --> %d\n", status); + goto fail2; + } + + return 0; + +fail2: + free_irq(spi->irq, ds1305); +fail1: + rtc_device_unregister(rtc); +fail0: + kfree(ds1305); + return status; +} + +static int __devexit ds1305_remove(struct spi_device *spi) +{ + struct ds1305 *ds1305 = spi_get_drvdata(spi); + + sysfs_remove_bin_file(&spi->dev.kobj, &nvram); + + /* carefully shut down irq and workqueue, if present */ + if (spi->irq) { + set_bit(FLAG_EXITING, &ds1305->flags); + free_irq(spi->irq, ds1305); + flush_scheduled_work(); + } + + rtc_device_unregister(ds1305->rtc); + spi_set_drvdata(spi, NULL); + kfree(ds1305); + return 0; +} + +static struct spi_driver ds1305_driver = { + .driver.name = "rtc-ds1305", + .driver.owner = THIS_MODULE, + .probe = ds1305_probe, + .remove = __devexit_p(ds1305_remove), + /* REVISIT add suspend/resume */ +}; + +static int __init ds1305_init(void) +{ + return spi_register_driver(&ds1305_driver); +} +module_init(ds1305_init); + +static void __exit ds1305_exit(void) +{ + spi_unregister_driver(&ds1305_driver); +} +module_exit(ds1305_exit); + +MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/spi/ds1305.h b/include/linux/spi/ds1305.h new file mode 100644 index 000000000000..287ec830eab7 --- /dev/null +++ b/include/linux/spi/ds1305.h @@ -0,0 +1,35 @@ +#ifndef __LINUX_SPI_DS1305_H +#define __LINUX_SPI_DS1305_H + +/* + * One-time configuration for ds1305 and ds1306 RTC chips. + * + * Put a pointer to this in spi_board_info.platform_data if you want to + * be sure that Linux (re)initializes this as needed ... after losing + * backup power, and potentially on the first boot. + */ +struct ds1305_platform_data { + + /* Trickle charge configuration: it's OK to leave out the MAGIC + * bitmask; mask in either DS1 or DS2, and then one of 2K/4k/8K. + */ +#define DS1305_TRICKLE_MAGIC 0xa0 +#define DS1305_TRICKLE_DS2 0x08 /* two diodes */ +#define DS1305_TRICKLE_DS1 0x04 /* one diode */ +#define DS1305_TRICKLE_2K 0x01 /* 2 KOhm resistance */ +#define DS1305_TRICKLE_4K 0x02 /* 4 KOhm resistance */ +#define DS1305_TRICKLE_8K 0x03 /* 8 KOhm resistance */ + u8 trickle; + + /* set only on ds1306 parts */ + bool is_ds1306; + + /* ds1306 only: enable 1 Hz output */ + bool en_1hz; + + /* REVISIT: the driver currently expects nINT0 to be wired + * as the alarm IRQ. ALM1 may also need to be set up ... + */ +}; + +#endif /* __LINUX_SPI_DS1305_H */ -- cgit From d3de851a445123f24ad8ece18662014b5e8a8b4e Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 23 Jul 2008 21:30:37 -0700 Subject: rtc: BCD codeshrink This updates to define the key routines as constant functions, which the macros will then call. Newer code can now call bcd2bin() instead of SCREAMING BCD2BIN() TO THE FOUR WINDS. This lets each driver shrink their codespace by using N function calls to a single (global) copy of those routines, instead of N inlined copies of these functions per driver. These routines aren't used in speed-critical code. Almost all callers are in the RTC framework. Typical per-driver savings is near 300 bytes. Signed-off-by: David Brownell Acked-by: Adrian Bunk Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bcd.h | 9 +++++++-- lib/Makefile | 2 +- lib/bcd.c | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 lib/bcd.c (limited to 'include/linux') diff --git a/include/linux/bcd.h b/include/linux/bcd.h index c545308125b0..7ac518e3c152 100644 --- a/include/linux/bcd.h +++ b/include/linux/bcd.h @@ -10,8 +10,13 @@ #ifndef _BCD_H #define _BCD_H -#define BCD2BIN(val) (((val) & 0x0f) + ((val)>>4)*10) -#define BIN2BCD(val) ((((val)/10)<<4) + (val)%10) +#include + +unsigned bcd2bin(unsigned char val) __attribute_const__; +unsigned char bin2bcd(unsigned val) __attribute_const__; + +#define BCD2BIN(val) bcd2bin(val) +#define BIN2BCD(val) bin2bcd(val) /* backwards compat */ #define BCD_TO_BIN(val) ((val)=BCD2BIN(val)) diff --git a/lib/Makefile b/lib/Makefile index 818c4d455518..9085ad6fa53d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -18,7 +18,7 @@ lib-$(CONFIG_SMP) += cpumask.o lib-y += kobject.o kref.o klist.o -obj-y += div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ +obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) diff --git a/lib/bcd.c b/lib/bcd.c new file mode 100644 index 000000000000..d74257fd0fe7 --- /dev/null +++ b/lib/bcd.c @@ -0,0 +1,14 @@ +#include +#include + +unsigned bcd2bin(unsigned char val) +{ + return (val & 0x0f) + (val >> 4) * 10; +} +EXPORT_SYMBOL(bcd2bin); + +unsigned char bin2bcd(unsigned val) +{ + return ((val / 10) << 4) + val % 10; +} +EXPORT_SYMBOL(bin2bcd); -- cgit From 01a2d9ed85c945fc8a672622780533a1a0b7caf5 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Wed, 23 Jul 2008 21:31:04 -0700 Subject: tridentfb: acceleration constants change This patch replaces deprecated constant FB_ACCELF_TEXT with FBINFO_HWACCEL_DISABLED and adds constants for Trident families of accelerators. The FBINFO_HWACCEL_DISABLED is correctly used so noaccel parameter works now. Signed-off-by: Krzysztof Helt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/tridentfb.c | 45 +++++++++++++++++++++++++++++++-------------- include/linux/fb.h | 4 ++++ 2 files changed, 35 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/tridentfb.c b/drivers/video/tridentfb.c index ff82ec1e5e4d..279411523ed9 100644 --- a/drivers/video/tridentfb.c +++ b/drivers/video/tridentfb.c @@ -499,6 +499,10 @@ static void tridentfb_fillrect(struct fb_info *info, struct tridentfb_par *par = info->par; int col; + if (info->flags & FBINFO_HWACCEL_DISABLED) { + cfb_fillrect(info, fr); + return; + } if (info->var.bits_per_pixel == 8) { col = fr->color; col |= col << 8; @@ -516,6 +520,10 @@ static void tridentfb_copyarea(struct fb_info *info, { struct tridentfb_par *par = info->par; + if (info->flags & FBINFO_HWACCEL_DISABLED) { + cfb_copyarea(info, ca); + return; + } par->wait_engine(par); par->copy_rect(par, ca->sx, ca->sy, ca->dx, ca->dy, ca->width, ca->height); @@ -525,7 +533,8 @@ static int tridentfb_sync(struct fb_info *info) { struct tridentfb_par *par = info->par; - par->wait_engine(par); + if (!(info->flags & FBINFO_HWACCEL_DISABLED)) + par->wait_engine(par); return 0; } #else @@ -855,8 +864,9 @@ static int tridentfb_check_var(struct fb_var_screeninfo *var, if (var->yres_virtual > 0xffff) return -EINVAL; line_length = var->xres_virtual * bpp / 8; -#ifdef CONFIG_FB_TRIDENT_ACCEL - if (!is3Dchip(par->chip_id)) { + + if (!is3Dchip(par->chip_id) && + !(info->flags & FBINFO_HWACCEL_DISABLED)) { /* acceleration requires line length to be power of 2 */ if (line_length <= 512) var->xres_virtual = 512 * 8 / bpp; @@ -873,7 +883,7 @@ static int tridentfb_check_var(struct fb_var_screeninfo *var, line_length = var->xres_virtual * bpp / 8; } -#endif + if (var->yres > var->yres_virtual) var->yres_virtual = var->yres; if (line_length * var->yres_virtual > info->fix.smem_len) @@ -1190,9 +1200,9 @@ static int tridentfb_set_par(struct fb_info *info) set_number_of_lines(par, info->var.yres); info->fix.line_length = info->var.xres_virtual * bpp / 8; set_lwidth(par, info->fix.line_length / 8); -#ifdef CONFIG_FB_TRIDENT_ACCEL - par->init_accel(par, info->var.xres_virtual, bpp); -#endif + + if (!(info->flags & FBINFO_HWACCEL_DISABLED)) + par->init_accel(par, info->var.xres_virtual, bpp); info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; info->cmap.len = (bpp == 8) ? 256 : 16; @@ -1326,6 +1336,9 @@ static int __devinit trident_pci_probe(struct pci_dev *dev, output("*** Please do use cyblafb, Cyberblade/i1 support " "will soon be removed from tridentfb!\n"); +#ifndef CONFIG_FB_TRIDENT_ACCEL + noaccel = 1; +#endif /* If PCI id is 0x9660 then further detect chip type */ @@ -1370,21 +1383,25 @@ static int __devinit trident_pci_probe(struct pci_dev *dev, default_par->wait_engine = xp_wait_engine; default_par->fill_rect = xp_fill_rect; default_par->copy_rect = xp_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADEXP; } else if (is_blade(chip_id)) { default_par->init_accel = blade_init_accel; default_par->wait_engine = blade_wait_engine; default_par->fill_rect = blade_fill_rect; default_par->copy_rect = blade_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADE3D; } else if (chip3D) { /* 3DImage family left */ default_par->init_accel = image_init_accel; default_par->wait_engine = image_wait_engine; default_par->fill_rect = image_fill_rect; default_par->copy_rect = image_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_3DIMAGE; } else { /* TGUI 9440/96XX family */ default_par->init_accel = tgui_init_accel; default_par->wait_engine = xp_wait_engine; default_par->fill_rect = tgui_fill_rect; default_par->copy_rect = tgui_copy_rect; + tridentfb_fix.accel = FB_ACCEL_TRIDENT_TGUI; } default_par->chip_id = chip_id; @@ -1441,9 +1458,13 @@ static int __devinit trident_pci_probe(struct pci_dev *dev, info->pseudo_palette = default_par->pseudo_pal; info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; -#ifdef CONFIG_FB_TRIDENT_ACCEL - info->flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; -#endif + if (!noaccel && default_par->init_accel) { + info->flags &= ~FBINFO_HWACCEL_DISABLED; + info->flags |= FBINFO_HWACCEL_COPYAREA; + info->flags |= FBINFO_HWACCEL_FILLRECT; + } else + info->flags |= FBINFO_HWACCEL_DISABLED; + if (!fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, bpp)) { err = -EINVAL; @@ -1453,10 +1474,6 @@ static int __devinit trident_pci_probe(struct pci_dev *dev, if (err < 0) goto out_unmap2; - if (!noaccel && default_par->init_accel) - info->var.accel_flags |= FB_ACCELF_TEXT; - else - info->var.accel_flags &= ~FB_ACCELF_TEXT; info->var.activate |= FB_ACTIVATE_NOW; info->device = &dev->dev; if (register_framebuffer(info) < 0) { diff --git a/include/linux/fb.h b/include/linux/fb.h index 72295b099228..a084d1335869 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -120,6 +120,10 @@ struct dentry; #define FB_ACCEL_XGI_VOLARI_V 47 /* XGI Volari V3XT, V5, V8 */ #define FB_ACCEL_XGI_VOLARI_Z 48 /* XGI Volari Z7 */ #define FB_ACCEL_OMAP1610 49 /* TI OMAP16xx */ +#define FB_ACCEL_TRIDENT_TGUI 50 /* Trident TGUI */ +#define FB_ACCEL_TRIDENT_3DIMAGE 51 /* Trident 3DImage */ +#define FB_ACCEL_TRIDENT_BLADE3D 52 /* Trident Blade3D */ +#define FB_ACCEL_TRIDENT_BLADEXP 53 /* Trident BladeXP */ #define FB_ACCEL_NEOMAGIC_NM2070 90 /* NeoMagic NM2070 */ #define FB_ACCEL_NEOMAGIC_NM2090 91 /* NeoMagic NM2090 */ #define FB_ACCEL_NEOMAGIC_NM2093 92 /* NeoMagic NM2093 */ -- cgit From 206c5d69d0540024faffd423fc703f1e457332d7 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 23 Jul 2008 21:31:35 -0700 Subject: sm501: add inversion controls for VBIASEN and FPEN Add flags to allow the driver to invert the sense of both VBIASEN and FPEN signals comming from the SM501. Signed-off-by: Ben Dooks Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/sm501fb.c | 26 ++++++++++++++++++++++---- include/linux/sm501.h | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index 15d4a768b1f6..122a0f8495c8 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -663,15 +663,25 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) sm501fb_sync_regs(fbi); mdelay(10); + /* VBIASEN */ + if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { - control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */ + if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) + control &= ~SM501_DC_PANEL_CONTROL_BIAS; + else + control |= SM501_DC_PANEL_CONTROL_BIAS; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { - control |= SM501_DC_PANEL_CONTROL_FPEN; + if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) + control &= ~SM501_DC_PANEL_CONTROL_FPEN; + else + control |= SM501_DC_PANEL_CONTROL_FPEN; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); @@ -679,14 +689,22 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) { /* disable panel power */ if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { - control &= ~SM501_DC_PANEL_CONTROL_FPEN; + if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) + control |= SM501_DC_PANEL_CONTROL_FPEN; + else + control &= ~SM501_DC_PANEL_CONTROL_FPEN; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); } if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { - control &= ~SM501_DC_PANEL_CONTROL_BIAS; + if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) + control |= SM501_DC_PANEL_CONTROL_BIAS; + else + control &= ~SM501_DC_PANEL_CONTROL_BIAS; + writel(control, ctrl_reg); sm501fb_sync_regs(fbi); mdelay(10); diff --git a/include/linux/sm501.h b/include/linux/sm501.h index 95c1c39ba445..b530fa6a1d34 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -73,6 +73,8 @@ extern unsigned long sm501_gpio_get(struct device *dev, #define SM501FB_FLAG_USE_HWACCEL (1<<3) #define SM501FB_FLAG_PANEL_NO_FPEN (1<<4) #define SM501FB_FLAG_PANEL_NO_VBIASEN (1<<5) +#define SM501FB_FLAG_PANEL_INV_FPEN (1<<6) +#define SM501FB_FLAG_PANEL_INV_VBIASEN (1<<7) struct sm501_platdata_fbsub { struct fb_videomode *def_mode; -- cgit From 0c531360ed504aa0ce995fcb8ef08e82b6534d0b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 23 Jul 2008 21:31:38 -0700 Subject: lcd: add lcd_device to check_fb() entry in lcd_ops Add the lcd_device being checked to the check_fb entry of lcd_ops. This ensures that any driver using this to check against it's own state can do so, and also makes all the calls in lcd_ops more orthogonal in their arguments. Signed-off-by: Ben Dooks Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/lcd.c | 2 +- drivers/video/bf54x-lq043fb.c | 2 +- drivers/video/bfin-t350mcqb-fb.c | 2 +- include/linux/lcd.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 299fd318dd45..b15b2b84a6f7 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -33,7 +33,7 @@ static int fb_notifier_callback(struct notifier_block *self, ld = container_of(self, struct lcd_device, fb_notif); mutex_lock(&ld->ops_lock); if (ld->ops) - if (!ld->ops->check_fb || ld->ops->check_fb(evdata->info)) + if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) ld->ops->set_power(ld, *(int *)evdata->data); mutex_unlock(&ld->ops_lock); return 0; diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c index 49834a67a623..940467aed13f 100644 --- a/drivers/video/bf54x-lq043fb.c +++ b/drivers/video/bf54x-lq043fb.c @@ -478,7 +478,7 @@ static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast) return 0; } -static int bfin_lcd_check_fb(struct fb_info *fi) +static int bfin_lcd_check_fb(struct lcd_device *dev, struct fb_info *fi) { if (!fi || (fi == &bfin_bf54x_fb)) return 1; diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c index 135d6dd7e672..7d1b819e501c 100644 --- a/drivers/video/bfin-t350mcqb-fb.c +++ b/drivers/video/bfin-t350mcqb-fb.c @@ -396,7 +396,7 @@ static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast) return 0; } -static int bfin_lcd_check_fb(struct fb_info *fi) +static int bfin_lcd_check_fb(struct lcd_device *dev, struct fb_info *fi) { if (!fi || (fi == &bfin_t350mcqb_fb)) return 1; diff --git a/include/linux/lcd.h b/include/linux/lcd.h index 1d379787f2e7..173febac6656 100644 --- a/include/linux/lcd.h +++ b/include/linux/lcd.h @@ -47,7 +47,7 @@ struct lcd_ops { int (*set_contrast)(struct lcd_device *, int contrast); /* Check if given framebuffer device is the one LCD is bound to; return 0 if not, !=0 if it is. If NULL, lcd always matches the fb. */ - int (*check_fb)(struct fb_info *); + int (*check_fb)(struct lcd_device *, struct fb_info *); }; struct lcd_device { -- cgit From 3e074058d72486676f6fdf6fe803200c62dcb403 Mon Sep 17 00:00:00 2001 From: Hans-Christian Egtvedt Date: Wed, 23 Jul 2008 21:31:48 -0700 Subject: fbdev: LCD backlight driver using Atmel PWM driver This patch adds a platform driver using the ATMEL PWM driver to control a backlight which requires a PWM signal and optional GPIO signal for discrete on/off signal. It has been tested on Favr-32 board from EarthLCD. The driver is configurable by supplying a struct with the platform data. See the include/linux/atmel-pwm-bl.h for details. The board code for Favr-32 will be submitted to the AVR32 kernel list. Signed-off-by: Hans-Christian Egtvedt Cc: Krzysztof Helt Cc: Haavard Skinnemoen Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/Kconfig | 12 ++ drivers/video/backlight/Makefile | 1 + drivers/video/backlight/atmel-pwm-bl.c | 244 +++++++++++++++++++++++++++++++++ include/linux/atmel-pwm-bl.h | 43 ++++++ 4 files changed, 300 insertions(+) create mode 100644 drivers/video/backlight/atmel-pwm-bl.c create mode 100644 include/linux/atmel-pwm-bl.h (limited to 'include/linux') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index b289e197e55d..98d9faf4970d 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -87,6 +87,18 @@ config BACKLIGHT_ATMEL_LCDC If in doubt, it's safe to enable this option; it doesn't kick in unless the board's description says it's wired that way. +config BACKLIGHT_ATMEL_PWM + tristate "Atmel PWM backlight control" + depends on BACKLIGHT_CLASS_DEVICE && ATMEL_PWM + default n + help + Say Y here if you want to use the PWM peripheral in Atmel AT91 and + AVR32 devices. This driver will need additional platform data to know + which PWM instance to use and how to configure it. + + To compile this driver as a module, choose M here: the module will be + called atmel-pwm-bl. + config BACKLIGHT_CORGI tristate "Generic (aka Sharp Corgi) Backlight Driver" depends on BACKLIGHT_CLASS_DEVICE diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 7d31c14088aa..d8a08e468cc7 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o +obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c new file mode 100644 index 000000000000..505c0823a105 --- /dev/null +++ b/drivers/video/backlight/atmel-pwm-bl.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2008 Atmel Corporation + * + * Backlight driver using Atmel PWM peripheral. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct atmel_pwm_bl { + const struct atmel_pwm_bl_platform_data *pdata; + struct backlight_device *bldev; + struct platform_device *pdev; + struct pwm_channel pwmc; + int gpio_on; +}; + +static int atmel_pwm_bl_set_intensity(struct backlight_device *bd) +{ + struct atmel_pwm_bl *pwmbl = bl_get_data(bd); + int intensity = bd->props.brightness; + int pwm_duty; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + if (pwmbl->pdata->pwm_active_low) + pwm_duty = pwmbl->pdata->pwm_duty_min + intensity; + else + pwm_duty = pwmbl->pdata->pwm_duty_max - intensity; + + if (pwm_duty > pwmbl->pdata->pwm_duty_max) + pwm_duty = pwmbl->pdata->pwm_duty_max; + if (pwm_duty < pwmbl->pdata->pwm_duty_min) + pwm_duty = pwmbl->pdata->pwm_duty_min; + + if (!intensity) { + if (pwmbl->gpio_on != -1) { + gpio_set_value(pwmbl->gpio_on, + 0 ^ pwmbl->pdata->on_active_low); + } + pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty); + pwm_channel_disable(&pwmbl->pwmc); + } else { + pwm_channel_enable(&pwmbl->pwmc); + pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty); + if (pwmbl->gpio_on != -1) { + gpio_set_value(pwmbl->gpio_on, + 1 ^ pwmbl->pdata->on_active_low); + } + } + + return 0; +} + +static int atmel_pwm_bl_get_intensity(struct backlight_device *bd) +{ + struct atmel_pwm_bl *pwmbl = bl_get_data(bd); + u8 intensity; + + if (pwmbl->pdata->pwm_active_low) { + intensity = pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY) - + pwmbl->pdata->pwm_duty_min; + } else { + intensity = pwmbl->pdata->pwm_duty_max - + pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY); + } + + return intensity; +} + +static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl) +{ + unsigned long pwm_rate = pwmbl->pwmc.mck; + unsigned long prescale = DIV_ROUND_UP(pwm_rate, + (pwmbl->pdata->pwm_frequency * + pwmbl->pdata->pwm_compare_max)) - 1; + + /* + * Prescale must be power of two and maximum 0xf in size because of + * hardware limit. PWM speed will be: + * PWM module clock speed / (2 ^ prescale). + */ + prescale = fls(prescale); + if (prescale > 0xf) + prescale = 0xf; + + pwm_channel_writel(&pwmbl->pwmc, PWM_CMR, prescale); + pwm_channel_writel(&pwmbl->pwmc, PWM_CDTY, + pwmbl->pdata->pwm_duty_min + + pwmbl->bldev->props.brightness); + pwm_channel_writel(&pwmbl->pwmc, PWM_CPRD, + pwmbl->pdata->pwm_compare_max); + + dev_info(&pwmbl->pdev->dev, "Atmel PWM backlight driver " + "(%lu Hz)\n", pwmbl->pwmc.mck / + pwmbl->pdata->pwm_compare_max / + (1 << prescale)); + + return pwm_channel_enable(&pwmbl->pwmc); +} + +static struct backlight_ops atmel_pwm_bl_ops = { + .get_brightness = atmel_pwm_bl_get_intensity, + .update_status = atmel_pwm_bl_set_intensity, +}; + +static int atmel_pwm_bl_probe(struct platform_device *pdev) +{ + const struct atmel_pwm_bl_platform_data *pdata; + struct backlight_device *bldev; + struct atmel_pwm_bl *pwmbl; + int retval; + + pwmbl = kzalloc(sizeof(struct atmel_pwm_bl), GFP_KERNEL); + if (!pwmbl) + return -ENOMEM; + + pwmbl->pdev = pdev; + + pdata = pdev->dev.platform_data; + if (!pdata) { + retval = -ENODEV; + goto err_free_mem; + } + + if (pdata->pwm_compare_max < pdata->pwm_duty_max || + pdata->pwm_duty_min > pdata->pwm_duty_max || + pdata->pwm_frequency == 0) { + retval = -EINVAL; + goto err_free_mem; + } + + pwmbl->pdata = pdata; + pwmbl->gpio_on = pdata->gpio_on; + + retval = pwm_channel_alloc(pdata->pwm_channel, &pwmbl->pwmc); + if (retval) + goto err_free_mem; + + if (pwmbl->gpio_on != -1) { + retval = gpio_request(pwmbl->gpio_on, "gpio_atmel_pwm_bl"); + if (retval) { + pwmbl->gpio_on = -1; + goto err_free_pwm; + } + + /* Turn display off by defatult. */ + retval = gpio_direction_output(pwmbl->gpio_on, + 0 ^ pdata->on_active_low); + if (retval) + goto err_free_gpio; + } + + bldev = backlight_device_register("atmel-pwm-bl", + &pdev->dev, pwmbl, &atmel_pwm_bl_ops); + if (IS_ERR(bldev)) { + retval = PTR_ERR(bldev); + goto err_free_gpio; + } + + pwmbl->bldev = bldev; + + platform_set_drvdata(pdev, pwmbl); + + /* Power up the backlight by default at middle intesity. */ + bldev->props.power = FB_BLANK_UNBLANK; + bldev->props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min; + bldev->props.brightness = bldev->props.max_brightness / 2; + + retval = atmel_pwm_bl_init_pwm(pwmbl); + if (retval) + goto err_free_bl_dev; + + atmel_pwm_bl_set_intensity(bldev); + + return 0; + +err_free_bl_dev: + platform_set_drvdata(pdev, NULL); + backlight_device_unregister(bldev); +err_free_gpio: + if (pwmbl->gpio_on != -1) + gpio_free(pwmbl->gpio_on); +err_free_pwm: + pwm_channel_free(&pwmbl->pwmc); +err_free_mem: + kfree(pwmbl); + return retval; +} + +static int __exit atmel_pwm_bl_remove(struct platform_device *pdev) +{ + struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev); + + if (pwmbl->gpio_on != -1) { + gpio_set_value(pwmbl->gpio_on, 0); + gpio_free(pwmbl->gpio_on); + } + pwm_channel_disable(&pwmbl->pwmc); + pwm_channel_free(&pwmbl->pwmc); + backlight_device_unregister(pwmbl->bldev); + platform_set_drvdata(pdev, NULL); + kfree(pwmbl); + + return 0; +} + +static struct platform_driver atmel_pwm_bl_driver = { + .driver = { + .name = "atmel-pwm-bl", + }, + /* REVISIT add suspend() and resume() */ + .remove = __exit_p(atmel_pwm_bl_remove), +}; + +static int __init atmel_pwm_bl_init(void) +{ + return platform_driver_probe(&atmel_pwm_bl_driver, atmel_pwm_bl_probe); +} +module_init(atmel_pwm_bl_init); + +static void __exit atmel_pwm_bl_exit(void) +{ + platform_driver_unregister(&atmel_pwm_bl_driver); +} +module_exit(atmel_pwm_bl_exit); + +MODULE_AUTHOR("Hans-Christian egtvedt "); +MODULE_DESCRIPTION("Atmel PWM backlight driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/atmel-pwm-bl.h b/include/linux/atmel-pwm-bl.h new file mode 100644 index 000000000000..0153a47806c2 --- /dev/null +++ b/include/linux/atmel-pwm-bl.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * Driver for the AT32AP700X PS/2 controller (PSIF). + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef __INCLUDE_ATMEL_PWM_BL_H +#define __INCLUDE_ATMEL_PWM_BL_H + +/** + * struct atmel_pwm_bl_platform_data + * @pwm_channel: which PWM channel in the PWM module to use. + * @pwm_frequency: PWM frequency to generate, the driver will try to be as + * close as the prescaler allows. + * @pwm_compare_max: value to use in the PWM channel compare register. + * @pwm_duty_max: maximum duty cycle value, must be less than or equal to + * pwm_compare_max. + * @pwm_duty_min: minimum duty cycle value, must be less than pwm_duty_max. + * @pwm_active_low: set to one if the low part of the PWM signal increases the + * brightness of the backlight. + * @gpio_on: GPIO line to control the backlight on/off, set to -1 if not used. + * @on_active_low: set to one if the on/off signal is on when GPIO is low. + * + * This struct must be added to the platform device in the board code. It is + * used by the atmel-pwm-bl driver to setup the GPIO to control on/off and the + * PWM device. + */ +struct atmel_pwm_bl_platform_data { + unsigned int pwm_channel; + unsigned int pwm_frequency; + unsigned int pwm_compare_max; + unsigned int pwm_duty_max; + unsigned int pwm_duty_min; + unsigned int pwm_active_low; + int gpio_on; + unsigned int on_active_low; +}; + +#endif /* __INCLUDE_ATMEL_PWM_BL_H */ -- cgit From 5bb49fcd501aa9fd3d321a22b7c01d9b0db7ab36 Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Wed, 23 Jul 2008 21:31:50 -0700 Subject: video/fb: cleanup FB_MAJOR usage Currently, linux/major.h defines a GRAPHDEV_MAJOR (29) that nobody uses, and linux/fb.h defines the real FB_MAJOR (also 29), that only fbmem.c needs. Drop GRAPHDEV_MAJOR from major.h, move FB_MAJOR definition from fb.h to major.h, and fix fbmem.c to use major.h's definition. Signed-off-by: Philippe De Muyter Cc: Krzysztof Helt Cc: "Antonino A. Daplas" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/fbmem.c | 1 + include/linux/fb.h | 1 - include/linux/major.h | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 6b487801eeae..5d84b3431098 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -35,6 +35,7 @@ #include #include #include +#include #include diff --git a/include/linux/fb.h b/include/linux/fb.h index a084d1335869..3b8870e32afd 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -8,7 +8,6 @@ struct dentry; /* Definitions of frame buffers */ -#define FB_MAJOR 29 #define FB_MAX 32 /* sufficient for now */ /* ioctls diff --git a/include/linux/major.h b/include/linux/major.h index 0cb98053537a..53d5fafd85c3 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -53,7 +53,7 @@ #define STL_SIOMEMMAJOR 28 #define ACSI_MAJOR 28 #define AZTECH_CDROM_MAJOR 29 -#define GRAPHDEV_MAJOR 29 /* SparcLinux & Linux/68k /dev/fb */ +#define FB_MAJOR 29 /* /dev/fb* framebuffers */ #define CM206_CDROM_MAJOR 32 #define IDE2_MAJOR 33 #define IDE3_MAJOR 34 -- cgit From f9247273cb69ba101877e946d2d83044409cc8c5 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Thu, 24 Jul 2008 17:22:13 +0100 Subject: UFS: add const to parser token table This patch adds a "const" to the parser token table. I've done an allmodconfig build to see if this produces any warnings/failures and the patch includes a fix for the only warning that was produced. Signed-off-by: Steven Whitehouse Acked-by: Alexander Viro Acked-by: Evgeniy Dushistov Signed-off-by: Linus Torvalds --- fs/ufs/super.c | 2 +- include/linux/parser.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 85b22b5977fa..506f724055c2 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1232,7 +1232,7 @@ static int ufs_show_options(struct seq_file *seq, struct vfsmount *vfs) { struct ufs_sb_info *sbi = UFS_SB(vfs->mnt_sb); unsigned mval = sbi->s_mount_opt & UFS_MOUNT_UFSTYPE; - struct match_token *tp = tokens; + const struct match_token *tp = tokens; while (tp->token != Opt_onerror_panic && tp->token != mval) ++tp; diff --git a/include/linux/parser.h b/include/linux/parser.h index 7dcd05075756..cc554ca8bc78 100644 --- a/include/linux/parser.h +++ b/include/linux/parser.h @@ -14,7 +14,7 @@ struct match_token { const char *pattern; }; -typedef struct match_token match_table_t[]; +typedef const struct match_token match_table_t[]; /* Maximum number of arguments that match_token will find in a pattern */ enum {MAX_OPT_ARGS = 3}; -- cgit From 6cdf6eb357c2681596b7b1672b92396ba82333d4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:14 +0200 Subject: ide: add ->dev and ->host_priv fields to struct ide_host * Add 'struct device *dev[2]' and 'void *host_priv' fields to struct ide_host. * Set ->dev[] in ide_host_alloc_all()/ide_setup_pci_device[s](). * Pass 'void *priv' argument to ide_setup_pci_device[s]() and use it to set ->host_priv. * Set PCI dev's ->driver_data to point to the struct ide_host instance if PCI host driver wants to use ->host_priv. * Rename ide_setup_pci_device[s]() to ide_pci_init_{one,two}(). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 3 +++ drivers/ide/pci/aec62xx.c | 2 +- drivers/ide/pci/alim15x3.c | 2 +- drivers/ide/pci/amd74xx.c | 2 +- drivers/ide/pci/atiixp.c | 2 +- drivers/ide/pci/cmd64x.c | 2 +- drivers/ide/pci/cs5530.c | 2 +- drivers/ide/pci/cs5535.c | 2 +- drivers/ide/pci/cy82c693.c | 2 +- drivers/ide/pci/generic.c | 2 +- drivers/ide/pci/hpt34x.c | 2 +- drivers/ide/pci/hpt366.c | 4 ++-- drivers/ide/pci/it8213.c | 2 +- drivers/ide/pci/it821x.c | 2 +- drivers/ide/pci/jmicron.c | 2 +- drivers/ide/pci/ns87415.c | 2 +- drivers/ide/pci/opti621.c | 2 +- drivers/ide/pci/pdc202xx_new.c | 4 ++-- drivers/ide/pci/pdc202xx_old.c | 2 +- drivers/ide/pci/piix.c | 2 +- drivers/ide/pci/rz1000.c | 2 +- drivers/ide/pci/sc1200.c | 2 +- drivers/ide/pci/serverworks.c | 2 +- drivers/ide/pci/siimage.c | 2 +- drivers/ide/pci/sis5513.c | 2 +- drivers/ide/pci/sl82c105.c | 2 +- drivers/ide/pci/slc90e66.c | 2 +- drivers/ide/pci/tc86c001.c | 2 +- drivers/ide/pci/triflex.c | 2 +- drivers/ide/pci/trm290.c | 2 +- drivers/ide/pci/via82cxxx.c | 2 +- drivers/ide/setup-pci.c | 52 ++++++++++++++++++++++++++++++++++++------ include/linux/ide.h | 7 ++++-- 33 files changed, 85 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 4aa76c453755..890c15b1b3ae 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1604,6 +1604,9 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, return NULL; } + if (hws[0]) + host->dev[0] = hws[0]->dev; + return host; } EXPORT_SYMBOL_GPL(ide_host_alloc_all); diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index fbc43e121e6b..7a5d246fe9b1 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -273,7 +273,7 @@ static int __devinit aec62xx_init_one(struct pci_dev *dev, const struct pci_devi } } - err = ide_setup_pci_device(dev, &d); + err = ide_pci_init_one(dev, &d, NULL); if (err) pci_disable_device(dev); diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 5ef7817ac64f..7f96e7ca3864 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -565,7 +565,7 @@ static int __devinit alim15x3_init_one(struct pci_dev *dev, const struct pci_dev if (idx == 0) d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX; - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index ef7d971031ee..b6a475313c7c 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -302,7 +302,7 @@ static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_ d.name, pci_name(dev), dev->revision, amd_dma[fls(d.udma_mask) - 1]); - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id amd74xx_pci_tbl[] = { diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index 8b637181681a..b483a68b39f6 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -167,7 +167,7 @@ static const struct ide_port_info atiixp_pci_info[] __devinitdata = { static int __devinit atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &atiixp_pci_info[id->driver_data]); + return ide_pci_init_one(dev, &atiixp_pci_info[id->driver_data], NULL); } static const struct pci_device_id atiixp_pci_tbl[] = { diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index ce58bfcdb3c6..fc0333c9a4e5 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -507,7 +507,7 @@ static int __devinit cmd64x_init_one(struct pci_dev *dev, const struct pci_devic } } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id cmd64x_pci_tbl[] = { diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index f5534c1ff349..ba82bad8bf4e 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -256,7 +256,7 @@ static const struct ide_port_info cs5530_chipset __devinitdata = { static int __devinit cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &cs5530_chipset); + return ide_pci_init_one(dev, &cs5530_chipset, NULL); } static const struct pci_device_id cs5530_pci_tbl[] = { diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c index 5404fe4f701d..2161f43ca1b8 100644 --- a/drivers/ide/pci/cs5535.c +++ b/drivers/ide/pci/cs5535.c @@ -180,7 +180,7 @@ static const struct ide_port_info cs5535_chipset __devinitdata = { static int __devinit cs5535_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &cs5535_chipset); + return ide_pci_init_one(dev, &cs5535_chipset, NULL); } static const struct pci_device_id cs5535_pci_tbl[] = { diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c index e14ad5530fa4..abd27ed7c30c 100644 --- a/drivers/ide/pci/cy82c693.c +++ b/drivers/ide/pci/cy82c693.c @@ -419,7 +419,7 @@ static int __devinit cy82c693_init_one(struct pci_dev *dev, const struct pci_dev if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && PCI_FUNC(dev->devfn) == 1) { dev2 = pci_get_slot(dev->bus, dev->devfn + 1); - ret = ide_setup_pci_devices(dev, dev2, &cy82c693_chipset); + ret = ide_pci_init_two(dev, dev2, &cy82c693_chipset, NULL); /* We leak pci refs here but thats ok - we can't be unloaded */ } return ret; diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c index 041720e22762..dd0caea5e4f3 100644 --- a/drivers/ide/pci/generic.c +++ b/drivers/ide/pci/generic.c @@ -139,7 +139,7 @@ static int __devinit generic_init_one(struct pci_dev *dev, const struct pci_devi goto out; } } - ret = ide_setup_pci_device(dev, d); + ret = ide_pci_init_one(dev, d, NULL); out: return ret; } diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index 9e1d1c4741da..3d70c5150ac6 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -156,7 +156,7 @@ static int __devinit hpt34x_init_one(struct pci_dev *dev, const struct pci_devic d = &hpt34x_chipsets[(pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0]; - return ide_setup_pci_device(dev, d); + return ide_pci_init_one(dev, d, NULL); } static const struct pci_device_id hpt34x_pci_tbl[] = { diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 1f1135ce7cd6..b23b7a278005 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -1608,13 +1608,13 @@ static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_devic d.host_flags &= ~IDE_HFLAG_NON_BOOTABLE; } - ret = ide_setup_pci_devices(dev, dev2, &d); + ret = ide_pci_init_two(dev, dev2, &d, NULL); if (ret < 0) pci_dev_put(dev2); return ret; } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id hpt366_pci_tbl[] __devinitconst = { diff --git a/drivers/ide/pci/it8213.c b/drivers/ide/pci/it8213.c index 2b71bdf74e73..18219fa9ef01 100644 --- a/drivers/ide/pci/it8213.c +++ b/drivers/ide/pci/it8213.c @@ -184,7 +184,7 @@ static const struct ide_port_info it8213_chipsets[] __devinitdata = { static int __devinit it8213_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &it8213_chipsets[id->driver_data]); + return ide_pci_init_one(dev, &it8213_chipsets[id->driver_data], NULL); } static const struct pci_device_id it8213_pci_tbl[] = { diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index cbf647202994..40186f9e56aa 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -664,7 +664,7 @@ static int __devinit it821x_init_one(struct pci_dev *dev, const struct pci_devic pci_set_drvdata(dev, itdevs); - return ide_setup_pci_device(dev, &it821x_chipsets[id->driver_data]); + return ide_pci_init_one(dev, &it821x_chipsets[id->driver_data], NULL); } static const struct pci_device_id it821x_pci_tbl[] = { diff --git a/drivers/ide/pci/jmicron.c b/drivers/ide/pci/jmicron.c index 96ef7394f283..a7e3c14f7b07 100644 --- a/drivers/ide/pci/jmicron.c +++ b/drivers/ide/pci/jmicron.c @@ -121,7 +121,7 @@ static const struct ide_port_info jmicron_chipset __devinitdata = { static int __devinit jmicron_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &jmicron_chipset); + return ide_pci_init_one(dev, &jmicron_chipset, NULL); } /* All JMB PATA controllers have and will continue to have the same diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 5cd2b32ff0ef..a45c33c0c792 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -324,7 +324,7 @@ static int __devinit ns87415_init_one(struct pci_dev *dev, const struct pci_devi d.tp_ops = &superio_tp_ops; } #endif - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id ns87415_pci_tbl[] = { diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c index 725c80508d90..edb9132ffbe4 100644 --- a/drivers/ide/pci/opti621.c +++ b/drivers/ide/pci/opti621.c @@ -209,7 +209,7 @@ static const struct ide_port_info opti621_chipset __devinitdata = { static int __devinit opti621_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &opti621_chipset); + return ide_pci_init_one(dev, &opti621_chipset, NULL); } static const struct pci_device_id opti621_pci_tbl[] = { diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c index 070df8ab3b21..71a420feb981 100644 --- a/drivers/ide/pci/pdc202xx_new.c +++ b/drivers/ide/pci/pdc202xx_new.c @@ -524,7 +524,7 @@ static int __devinit pdc202new_init_one(struct pci_dev *dev, const struct pci_de dev2 = pdc20270_get_dev2(dev); if (dev2) { - int ret = ide_setup_pci_devices(dev, dev2, d); + int ret = ide_pci_init_two(dev, dev2, d, NULL); if (ret < 0) pci_dev_put(dev2); return ret; @@ -540,7 +540,7 @@ static int __devinit pdc202new_init_one(struct pci_dev *dev, const struct pci_de return -ENODEV; } - return ide_setup_pci_device(dev, d); + return ide_pci_init_one(dev, d, NULL); } static const struct pci_device_id pdc202new_pci_tbl[] = { diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index e54dc653b8c4..eba1d60a73a0 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -412,7 +412,7 @@ static int __devinit pdc202xx_init_one(struct pci_dev *dev, const struct pci_dev } } - return ide_setup_pci_device(dev, d); + return ide_pci_init_one(dev, d, NULL); } static const struct pci_device_id pdc202xx_pci_tbl[] = { diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index 0ce41b4dddaf..359f65ddcbf9 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -394,7 +394,7 @@ static const struct ide_port_info piix_pci_info[] __devinitdata = { static int __devinit piix_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &piix_pci_info[id->driver_data]); + return ide_pci_init_one(dev, &piix_pci_info[id->driver_data], NULL); } /** diff --git a/drivers/ide/pci/rz1000.c b/drivers/ide/pci/rz1000.c index 532154adba29..860ffdeca095 100644 --- a/drivers/ide/pci/rz1000.c +++ b/drivers/ide/pci/rz1000.c @@ -48,7 +48,7 @@ static const struct ide_port_info rz1000_chipset __devinitdata = { static int __devinit rz1000_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &rz1000_chipset); + return ide_pci_init_one(dev, &rz1000_chipset, NULL); } static const struct pci_device_id rz1000_pci_tbl[] = { diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c index 14c787b5d95f..8fd9cc2119d6 100644 --- a/drivers/ide/pci/sc1200.c +++ b/drivers/ide/pci/sc1200.c @@ -317,7 +317,7 @@ static const struct ide_port_info sc1200_chipset __devinitdata = { static int __devinit sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &sc1200_chipset); + return ide_pci_init_one(dev, &sc1200_chipset, NULL); } static const struct pci_device_id sc1200_pci_tbl[] = { diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index 127ccb45e261..34abdfc8d567 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -422,7 +422,7 @@ static int __devinit svwks_init_one(struct pci_dev *dev, const struct pci_device d.host_flags &= ~IDE_HFLAG_SINGLE; } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id svwks_pci_tbl[] = { diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 5965a35d94ae..48124133601a 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -795,7 +795,7 @@ static int __devinit siimage_init_one(struct pci_dev *dev, d.host_flags |= IDE_HFLAG_NO_ATAPI_DMA; } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id siimage_pci_tbl[] = { diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c index 2389945ca95d..a2330c4ac75b 100644 --- a/drivers/ide/pci/sis5513.c +++ b/drivers/ide/pci/sis5513.c @@ -583,7 +583,7 @@ static int __devinit sis5513_init_one(struct pci_dev *dev, const struct pci_devi d.udma_mask = udma_rates[chipset_family]; - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id sis5513_pci_tbl[] = { diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index f82a6502c1b7..be22f8125d71 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -335,7 +335,7 @@ static int __devinit sl82c105_init_one(struct pci_dev *dev, const struct pci_dev d.host_flags &= ~IDE_HFLAG_SERIALIZE_DMA; } - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id sl82c105_pci_tbl[] = { diff --git a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c index dae6e2c94d86..2fc2f2cf2206 100644 --- a/drivers/ide/pci/slc90e66.c +++ b/drivers/ide/pci/slc90e66.c @@ -144,7 +144,7 @@ static const struct ide_port_info slc90e66_chipset __devinitdata = { static int __devinit slc90e66_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &slc90e66_chipset); + return ide_pci_init_one(dev, &slc90e66_chipset, NULL); } static const struct pci_device_id slc90e66_pci_tbl[] = { diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c index 477e19790102..e16e79d21772 100644 --- a/drivers/ide/pci/tc86c001.c +++ b/drivers/ide/pci/tc86c001.c @@ -215,7 +215,7 @@ static const struct ide_port_info tc86c001_chipset __devinitdata = { static int __devinit tc86c001_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &tc86c001_chipset); + return ide_pci_init_one(dev, &tc86c001_chipset, NULL); } static const struct pci_device_id tc86c001_pci_tbl[] = { diff --git a/drivers/ide/pci/triflex.c b/drivers/ide/pci/triflex.c index db65a558d4ec..60dcb645d1b0 100644 --- a/drivers/ide/pci/triflex.c +++ b/drivers/ide/pci/triflex.c @@ -104,7 +104,7 @@ static const struct ide_port_info triflex_device __devinitdata = { static int __devinit triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &triflex_device); + return ide_pci_init_one(dev, &triflex_device, NULL); } static const struct pci_device_id triflex_pci_tbl[] = { diff --git a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c index a8a3138682ef..d8127b51a542 100644 --- a/drivers/ide/pci/trm290.c +++ b/drivers/ide/pci/trm290.c @@ -340,7 +340,7 @@ static const struct ide_port_info trm290_chipset __devinitdata = { static int __devinit trm290_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - return ide_setup_pci_device(dev, &trm290_chipset); + return ide_pci_init_one(dev, &trm290_chipset, NULL); } static const struct pci_device_id trm290_pci_tbl[] = { diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index 09dc4803ef9d..2f22abfe003b 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -466,7 +466,7 @@ static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_i d.udma_mask = via_config->udma_mask; - return ide_setup_pci_device(dev, &d); + return ide_pci_init_one(dev, &d, NULL); } static const struct pci_device_id via_pci_tbl[] = { diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index b85de71fdc88..ca17bf8896df 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -525,8 +525,10 @@ out: return ret; } -int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) +int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d, + void *priv) { + struct ide_host *host; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; int ret; @@ -536,6 +538,19 @@ int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) ide_pci_setup_ports(dev, d, 0, &hw[0], &hws[0]); + host = ide_host_alloc(d, hws); + if (host == NULL) { + ret = -ENOMEM; + goto out; + } + + host->dev[0] = &dev->dev; + + host->host_priv = priv; + + if (priv) + pci_set_drvdata(dev, host); + ret = do_ide_setup_pci_device(dev, d, 1); if (ret < 0) goto out; @@ -543,16 +558,19 @@ int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d) /* fixup IRQ */ hw[1].irq = hw[0].irq = ret; - ret = ide_host_add(d, hws, NULL); + ret = ide_host_register(host, d, hws); + if (ret) + ide_host_free(host); out: return ret; } -EXPORT_SYMBOL_GPL(ide_setup_pci_device); +EXPORT_SYMBOL_GPL(ide_pci_init_one); -int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, - const struct ide_port_info *d) +int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2, + const struct ide_port_info *d, void *priv) { struct pci_dev *pdev[] = { dev1, dev2 }; + struct ide_host *host; int ret, i; hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL }; @@ -562,7 +580,25 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, goto out; ide_pci_setup_ports(pdev[i], d, 0, &hw[i*2], &hws[i*2]); + } + host = ide_host_alloc(d, hws); + if (host == NULL) { + ret = -ENOMEM; + goto out; + } + + host->dev[0] = &dev1->dev; + host->dev[1] = &dev2->dev; + + host->host_priv = priv; + + if (priv) { + pci_set_drvdata(pdev[0], host); + pci_set_drvdata(pdev[1], host); + } + + for (i = 0; i < 2; i++) { ret = do_ide_setup_pci_device(pdev[i], d, !i); /* @@ -576,8 +612,10 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2, hw[i*2 + 1].irq = hw[i*2].irq = ret; } - ret = ide_host_add(d, hws, NULL); + ret = ide_host_register(host, d, hws); + if (ret) + ide_host_free(host); out: return ret; } -EXPORT_SYMBOL_GPL(ide_setup_pci_devices); +EXPORT_SYMBOL_GPL(ide_pci_init_two); diff --git a/include/linux/ide.h b/include/linux/ide.h index d67ccca2b964..776c574c9640 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -626,6 +626,8 @@ typedef struct hwif_s { struct ide_host { ide_hwif_t *ports[MAX_HWIFS]; unsigned int n_ports; + struct device *dev[2]; + void *host_priv; }; /* @@ -1201,8 +1203,9 @@ struct ide_port_info { u8 udma_mask; }; -int ide_setup_pci_device(struct pci_dev *, const struct ide_port_info *); -int ide_setup_pci_devices(struct pci_dev *, struct pci_dev *, const struct ide_port_info *); +int ide_pci_init_one(struct pci_dev *, const struct ide_port_info *, void *); +int ide_pci_init_two(struct pci_dev *, struct pci_dev *, + const struct ide_port_info *, void *); void ide_map_sg(ide_drive_t *, struct request *); void ide_init_sg_cmd(ide_drive_t *, struct request *); -- cgit From 08da591e14cf87247ec09b17c350235157a92fc3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:15 +0200 Subject: ide: add ide_device_{get,put}() helpers * Add 'struct ide_host *host' field to ide_hwif_t and set it in ide_host_alloc_all(). * Add ide_device_{get,put}() helpers loosely based on SCSI's scsi_device_{get,put}() ones. * Convert IDE device drivers to use ide_device_{get,put}(). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-cd.c | 12 +++++++++--- drivers/ide/ide-disk.c | 12 +++++++++--- drivers/ide/ide-floppy.c | 12 +++++++++--- drivers/ide/ide-probe.c | 2 ++ drivers/ide/ide-tape.c | 12 +++++++++--- drivers/ide/ide.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/ide-scsi.c | 8 +++++++- include/linux/ide.h | 7 +++++++ 8 files changed, 99 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 4e73aeee4053..8f253e5f26a8 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -57,23 +57,29 @@ static DEFINE_MUTEX(idecd_ref_mutex); #define ide_cd_g(disk) \ container_of((disk)->private_data, struct cdrom_info, driver) +static void ide_cd_release(struct kref *); + static struct cdrom_info *ide_cd_get(struct gendisk *disk) { struct cdrom_info *cd = NULL; mutex_lock(&idecd_ref_mutex); cd = ide_cd_g(disk); - if (cd) + if (cd) { kref_get(&cd->kref); + if (ide_device_get(cd->drive)) { + kref_put(&cd->kref, ide_cd_release); + cd = NULL; + } + } mutex_unlock(&idecd_ref_mutex); return cd; } -static void ide_cd_release(struct kref *); - static void ide_cd_put(struct cdrom_info *cd) { mutex_lock(&idecd_ref_mutex); + ide_device_put(cd->drive); kref_put(&cd->kref, ide_cd_release); mutex_unlock(&idecd_ref_mutex); } diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index df5fe5756871..28d85b410f7c 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -56,23 +56,29 @@ static DEFINE_MUTEX(idedisk_ref_mutex); #define ide_disk_g(disk) \ container_of((disk)->private_data, struct ide_disk_obj, driver) +static void ide_disk_release(struct kref *); + static struct ide_disk_obj *ide_disk_get(struct gendisk *disk) { struct ide_disk_obj *idkp = NULL; mutex_lock(&idedisk_ref_mutex); idkp = ide_disk_g(disk); - if (idkp) + if (idkp) { kref_get(&idkp->kref); + if (ide_device_get(idkp->drive)) { + kref_put(&idkp->kref, ide_disk_release); + idkp = NULL; + } + } mutex_unlock(&idedisk_ref_mutex); return idkp; } -static void ide_disk_release(struct kref *); - static void ide_disk_put(struct ide_disk_obj *idkp) { mutex_lock(&idedisk_ref_mutex); + ide_device_put(idkp->drive); kref_put(&idkp->kref, ide_disk_release); mutex_unlock(&idedisk_ref_mutex); } diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 3d8e6dd0f41e..ca11a26746f1 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -158,23 +158,29 @@ static DEFINE_MUTEX(idefloppy_ref_mutex); #define ide_floppy_g(disk) \ container_of((disk)->private_data, struct ide_floppy_obj, driver) +static void idefloppy_cleanup_obj(struct kref *); + static struct ide_floppy_obj *ide_floppy_get(struct gendisk *disk) { struct ide_floppy_obj *floppy = NULL; mutex_lock(&idefloppy_ref_mutex); floppy = ide_floppy_g(disk); - if (floppy) + if (floppy) { kref_get(&floppy->kref); + if (ide_device_get(floppy->drive)) { + kref_put(&floppy->kref, idefloppy_cleanup_obj); + floppy = NULL; + } + } mutex_unlock(&idefloppy_ref_mutex); return floppy; } -static void idefloppy_cleanup_obj(struct kref *); - static void ide_floppy_put(struct ide_floppy_obj *floppy) { mutex_lock(&idefloppy_ref_mutex); + ide_device_put(floppy->drive); kref_put(&floppy->kref, idefloppy_cleanup_obj); mutex_unlock(&idefloppy_ref_mutex); } diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 890c15b1b3ae..9ab5892eaea1 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1595,6 +1595,8 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, ide_init_port_data(hwif, idx); + hwif->host = host; + host->ports[i] = hwif; host->n_ports++; } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 6962ca4891a1..789f3428f072 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -322,23 +322,29 @@ static struct class *idetape_sysfs_class; #define ide_tape_g(disk) \ container_of((disk)->private_data, struct ide_tape_obj, driver) +static void ide_tape_release(struct kref *); + static struct ide_tape_obj *ide_tape_get(struct gendisk *disk) { struct ide_tape_obj *tape = NULL; mutex_lock(&idetape_ref_mutex); tape = ide_tape_g(disk); - if (tape) + if (tape) { kref_get(&tape->kref); + if (ide_device_get(tape->drive)) { + kref_put(&tape->kref, ide_tape_release); + tape = NULL; + } + } mutex_unlock(&idetape_ref_mutex); return tape; } -static void ide_tape_release(struct kref *); - static void ide_tape_put(struct ide_tape_obj *tape) { mutex_lock(&idetape_ref_mutex); + ide_device_put(tape->drive); kref_put(&tape->kref, ide_tape_release); mutex_unlock(&idetape_ref_mutex); } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 60f0ca66aa93..772451600e4d 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -618,6 +618,53 @@ set_val: EXPORT_SYMBOL(generic_ide_ioctl); +/** + * ide_device_get - get an additional reference to a ide_drive_t + * @drive: device to get a reference to + * + * Gets a reference to the ide_drive_t and increments the use count of the + * underlying LLDD module. + */ +int ide_device_get(ide_drive_t *drive) +{ + struct device *host_dev; + struct module *module; + + if (!get_device(&drive->gendev)) + return -ENXIO; + + host_dev = drive->hwif->host->dev[0]; + module = host_dev ? host_dev->driver->owner : NULL; + + if (module && !try_module_get(module)) { + put_device(&drive->gendev); + return -ENXIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ide_device_get); + +/** + * ide_device_put - release a reference to a ide_drive_t + * @drive: device to release a reference on + * + * Release a reference to the ide_drive_t and decrements the use count of + * the underlying LLDD module. + */ +void ide_device_put(ide_drive_t *drive) +{ +#ifdef CONFIG_MODULE_UNLOAD + struct device *host_dev = drive->hwif->host->dev[0]; + struct module *module = host_dev ? host_dev->driver->owner : NULL; + + if (module) + module_put(module); +#endif + put_device(&drive->gendev); +} +EXPORT_SYMBOL_GPL(ide_device_put); + static int ide_bus_match(struct device *dev, struct device_driver *drv) { return 1; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 538552495d48..318ef382448f 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -101,8 +101,13 @@ static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk) mutex_lock(&idescsi_ref_mutex); scsi = ide_scsi_g(disk); - if (scsi) + if (scsi) { scsi_host_get(scsi->host); + if (ide_device_get(scsi->drive)) { + scsi_host_put(scsi->host); + scsi = NULL; + } + } mutex_unlock(&idescsi_ref_mutex); return scsi; } @@ -110,6 +115,7 @@ static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk) static void ide_scsi_put(struct ide_scsi_obj *scsi) { mutex_lock(&idescsi_ref_mutex); + ide_device_put(scsi->drive); scsi_host_put(scsi->host); mutex_unlock(&idescsi_ref_mutex); } diff --git a/include/linux/ide.h b/include/linux/ide.h index 776c574c9640..3eccac0a2a36 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -532,12 +532,16 @@ struct ide_dma_ops { void (*dma_timeout)(struct ide_drive_s *); }; +struct ide_host; + typedef struct hwif_s { struct hwif_s *next; /* for linked-list in ide_hwgroup_t */ struct hwif_s *mate; /* other hwif from same PCI chip */ struct hwgroup_s *hwgroup; /* actually (ide_hwgroup_t *) */ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ + struct ide_host *host; + char name[6]; /* name of interface, eg. "ide0" */ struct ide_io_ports io_ports; @@ -876,6 +880,9 @@ struct ide_driver_s { #define to_ide_driver(drv) container_of(drv, ide_driver_t, gen_driver) +int ide_device_get(ide_drive_t *); +void ide_device_put(ide_drive_t *); + int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *, unsigned, unsigned long); extern int ide_vlb_clk; -- cgit From ef0b04276d8f719d754c092434fbd62c2aeb5307 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:19 +0200 Subject: ide: add ide_pci_remove() helper * Add 'unsigned long host_flags' field to struct ide_host. * Set ->host_flags in ide_host_alloc_all(). * Always set PCI dev's ->driver_data in ide_pci_init_{one,two}(). * Add ide_pci_remove() helper (the default implementation for struct pci_driver's ->remove method). Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 3 +++ drivers/ide/setup-pci.c | 39 +++++++++++++++++++++++++++++++++------ include/linux/ide.h | 2 ++ 3 files changed, 38 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 9ab5892eaea1..f0c162488ec4 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1609,6 +1609,9 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d, if (hws[0]) host->dev[0] = hws[0]->dev; + if (d) + host->host_flags = d->host_flags; + return host; } EXPORT_SYMBOL_GPL(ide_host_alloc_all); diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index ca17bf8896df..20f0ee004695 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -548,8 +548,7 @@ int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d, host->host_priv = priv; - if (priv) - pci_set_drvdata(dev, host); + pci_set_drvdata(dev, host); ret = do_ide_setup_pci_device(dev, d, 1); if (ret < 0) @@ -593,10 +592,8 @@ int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2, host->host_priv = priv; - if (priv) { - pci_set_drvdata(pdev[0], host); - pci_set_drvdata(pdev[1], host); - } + pci_set_drvdata(pdev[0], host); + pci_set_drvdata(pdev[1], host); for (i = 0; i < 2; i++) { ret = do_ide_setup_pci_device(pdev[i], d, !i); @@ -619,3 +616,33 @@ out: return ret; } EXPORT_SYMBOL_GPL(ide_pci_init_two); + +void ide_pci_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL; + int bars; + + if (host->host_flags & IDE_HFLAG_SINGLE) + bars = (1 << 2) - 1; + else + bars = (1 << 4) - 1; + + if ((host->host_flags & IDE_HFLAG_NO_DMA) == 0) { + if (host->host_flags & IDE_HFLAG_CS5520) + bars |= (1 << 2); + else + bars |= (1 << 4); + } + + ide_host_remove(host); + + if (dev2) + pci_release_selected_regions(dev2, bars); + pci_release_selected_regions(dev, bars); + + if (dev2) + pci_disable_device(dev2); + pci_disable_device(dev); +} +EXPORT_SYMBOL_GPL(ide_pci_remove); diff --git a/include/linux/ide.h b/include/linux/ide.h index 3eccac0a2a36..dbd0aeb3a56d 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -631,6 +631,7 @@ struct ide_host { ide_hwif_t *ports[MAX_HWIFS]; unsigned int n_ports; struct device *dev[2]; + unsigned long host_flags; void *host_priv; }; @@ -1213,6 +1214,7 @@ struct ide_port_info { int ide_pci_init_one(struct pci_dev *, const struct ide_port_info *, void *); int ide_pci_init_two(struct pci_dev *, struct pci_dev *, const struct ide_port_info *, void *); +void ide_pci_remove(struct pci_dev *); void ide_map_sg(ide_drive_t *, struct request *); void ide_init_sg_cmd(ide_drive_t *, struct request *); -- cgit From d83b8b85cd56a083d30df73f3fd5e4714591b910 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:30 +0200 Subject: ide: define MAX_HWIFS in * Now that ide_hwif_t instances are allocated dynamically the difference between MAX_HWIFS == 2 and MAX_HWIFS == 10 is ~100 bytes (x86-32) so use MAX_HWIFS == 10 on all archs except these ones that use MAX_HWIFS == 1. * Define MAX_HWIFS in instead of . [ Please note that avr32/cris/v850 have no and alpha/ia64/sh always define CONFIG_IDE_MAX_HWIFS. ] Signed-off-by: Bartlomiej Zolnierkiewicz --- include/asm-arm/ide.h | 4 ---- include/asm-blackfin/ide.h | 2 -- include/asm-frv/ide.h | 4 ---- include/asm-h8300/ide.h | 2 -- include/asm-m32r/ide.h | 8 -------- include/asm-m68k/ide.h | 4 ---- include/asm-mips/mach-generic/ide.h | 8 -------- include/asm-mn10300/ide.h | 4 ---- include/asm-parisc/ide.h | 4 ---- include/asm-powerpc/ide.h | 8 -------- include/asm-sparc/ide.h | 3 --- include/asm-x86/ide.h | 9 --------- include/asm-xtensa/ide.h | 5 ----- include/linux/ide.h | 8 ++++++++ 14 files changed, 8 insertions(+), 65 deletions(-) (limited to 'include/linux') diff --git a/include/asm-arm/ide.h b/include/asm-arm/ide.h index 88f4d231ce4f..a48019f99d08 100644 --- a/include/asm-arm/ide.h +++ b/include/asm-arm/ide.h @@ -13,10 +13,6 @@ #ifdef __KERNEL__ -#ifndef MAX_HWIFS -#define MAX_HWIFS 4 -#endif - #define __ide_mm_insw(port,addr,len) readsw(port,addr,len) #define __ide_mm_insl(port,addr,len) readsl(port,addr,len) #define __ide_mm_outsw(port,addr,len) writesw(port,addr,len) diff --git a/include/asm-blackfin/ide.h b/include/asm-blackfin/ide.h index 5b88de115bf4..90bc50bd22e9 100644 --- a/include/asm-blackfin/ide.h +++ b/include/asm-blackfin/ide.h @@ -17,8 +17,6 @@ #ifdef __KERNEL__ /****************************************************************************/ -#define MAX_HWIFS 1 - #include /****************************************************************************/ diff --git a/include/asm-frv/ide.h b/include/asm-frv/ide.h index 8c9a540d4344..7ebcc56a2229 100644 --- a/include/asm-frv/ide.h +++ b/include/asm-frv/ide.h @@ -18,10 +18,6 @@ #include #include -#ifndef MAX_HWIFS -#define MAX_HWIFS 8 -#endif - /****************************************************************************/ /* * some bits needed for parts of the IDE subsystem to compile diff --git a/include/asm-h8300/ide.h b/include/asm-h8300/ide.h index f8535ce7476e..8f79ba2ff929 100644 --- a/include/asm-h8300/ide.h +++ b/include/asm-h8300/ide.h @@ -16,8 +16,6 @@ #ifdef __KERNEL__ /****************************************************************************/ -#define MAX_HWIFS 1 - #include /****************************************************************************/ diff --git a/include/asm-m32r/ide.h b/include/asm-m32r/ide.h index 72798d624221..d755d41b9931 100644 --- a/include/asm-m32r/ide.h +++ b/include/asm-m32r/ide.h @@ -15,14 +15,6 @@ #include -#ifndef MAX_HWIFS -# ifdef CONFIG_BLK_DEV_IDEPCI -#define MAX_HWIFS 10 -# else -#define MAX_HWIFS 2 -# endif -#endif - static __inline__ int ide_default_irq(unsigned long base) { switch (base) { diff --git a/include/asm-m68k/ide.h b/include/asm-m68k/ide.h index 909c6dfd3851..1daf6cbdd9f0 100644 --- a/include/asm-m68k/ide.h +++ b/include/asm-m68k/ide.h @@ -45,10 +45,6 @@ #include #endif -#ifndef MAX_HWIFS -#define MAX_HWIFS 4 /* same as the other archs */ -#endif - /* * Get rid of defs from io.h - ide has its private and conflicting versions * Since so far no single m68k platform uses ISA/PCI I/O space for IDE, we diff --git a/include/asm-mips/mach-generic/ide.h b/include/asm-mips/mach-generic/ide.h index f34740ee6775..8ee6bff030d9 100644 --- a/include/asm-mips/mach-generic/ide.h +++ b/include/asm-mips/mach-generic/ide.h @@ -19,14 +19,6 @@ #include #include -#ifndef MAX_HWIFS -# ifdef CONFIG_BLK_DEV_IDEPCI -#define MAX_HWIFS 10 -# else -#define MAX_HWIFS 6 -# endif -#endif - static __inline__ int ide_probe_legacy(void) { #ifdef CONFIG_PCI diff --git a/include/asm-mn10300/ide.h b/include/asm-mn10300/ide.h index dc235121ec42..6adcdd92e83d 100644 --- a/include/asm-mn10300/ide.h +++ b/include/asm-mn10300/ide.h @@ -23,10 +23,6 @@ #undef SUPPORT_VLB_SYNC #define SUPPORT_VLB_SYNC 0 -#ifndef MAX_HWIFS -#define MAX_HWIFS 8 -#endif - /* * some bits needed for parts of the IDE subsystem to compile */ diff --git a/include/asm-parisc/ide.h b/include/asm-parisc/ide.h index db0c94410095..c246ef75017d 100644 --- a/include/asm-parisc/ide.h +++ b/include/asm-parisc/ide.h @@ -13,10 +13,6 @@ #ifdef __KERNEL__ -#ifndef MAX_HWIFS -#define MAX_HWIFS 2 -#endif - #define ide_request_irq(irq,hand,flg,dev,id) request_irq((irq),(hand),(flg),(dev),(id)) #define ide_free_irq(irq,dev_id) free_irq((irq), (dev_id)) #define ide_request_region(from,extent,name) request_region((from), (extent), (name)) diff --git a/include/asm-powerpc/ide.h b/include/asm-powerpc/ide.h index 3d90bf7d3d73..262def6a9f0a 100644 --- a/include/asm-powerpc/ide.h +++ b/include/asm-powerpc/ide.h @@ -14,14 +14,6 @@ #endif #include -#ifndef MAX_HWIFS -#ifdef __powerpc64__ -#define MAX_HWIFS 10 -#else -#define MAX_HWIFS 8 -#endif -#endif - #define __ide_mm_insw(p, a, c) readsw((void __iomem *)(p), (a), (c)) #define __ide_mm_insl(p, a, c) readsl((void __iomem *)(p), (a), (c)) #define __ide_mm_outsw(p, a, c) writesw((void __iomem *)(p), (a), (c)) diff --git a/include/asm-sparc/ide.h b/include/asm-sparc/ide.h index 879fcec72dc1..b7af3d658239 100644 --- a/include/asm-sparc/ide.h +++ b/include/asm-sparc/ide.h @@ -21,9 +21,6 @@ #include #endif -#undef MAX_HWIFS -#define MAX_HWIFS 2 - #define __ide_insl(data_reg, buffer, wcount) \ __ide_insw(data_reg, buffer, (wcount)<<1) #define __ide_outsl(data_reg, buffer, wcount) \ diff --git a/include/asm-x86/ide.h b/include/asm-x86/ide.h index 34050747f38c..bc54879daed1 100644 --- a/include/asm-x86/ide.h +++ b/include/asm-x86/ide.h @@ -11,15 +11,6 @@ #ifdef __KERNEL__ - -#ifndef MAX_HWIFS -# ifdef CONFIG_BLK_DEV_IDEPCI -#define MAX_HWIFS 10 -# else -#define MAX_HWIFS 6 -# endif -#endif - static __inline__ int ide_default_irq(unsigned long base) { switch (base) { diff --git a/include/asm-xtensa/ide.h b/include/asm-xtensa/ide.h index cb995701c42d..18342a2cc774 100644 --- a/include/asm-xtensa/ide.h +++ b/include/asm-xtensa/ide.h @@ -14,11 +14,6 @@ #ifdef __KERNEL__ - -#ifndef MAX_HWIFS -# define MAX_HWIFS 1 -#endif - #include #endif /* __KERNEL__ */ diff --git a/include/linux/ide.h b/include/linux/ide.h index dbd0aeb3a56d..76fe00b24b51 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -213,6 +213,14 @@ static inline int __ide_default_irq(unsigned long base) #include +#ifndef MAX_HWIFS +#if defined(CONFIG_BLACKFIN) || defined(CONFIG_H8300) || defined(CONFIG_XTENSA) +# define MAX_HWIFS 1 +#else +# define MAX_HWIFS 10 +#endif +#endif + #if !defined(MAX_HWIFS) || defined(CONFIG_EMBEDDED) #undef MAX_HWIFS #define MAX_HWIFS CONFIG_IDE_MAX_HWIFS -- cgit From 2a8f7450f828eaee49d66f41f99ac2e54f1160a6 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:31 +0200 Subject: ide: remove for some archs * Remove include from ( includes which is enough). * Remove for alpha/blackfin/h8300/ia64/m32r/sh/x86/xtensa (this leaves us with arm/frv/m68k/mips/mn10300/parisc/powerpc/sparc[64]). There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- include/asm-alpha/ide.h | 20 -------------------- include/asm-blackfin/ide.h | 25 ------------------------- include/asm-h8300/ide.h | 24 ------------------------ include/asm-ia64/ide.h | 22 ---------------------- include/asm-m32r/ide.h | 20 -------------------- include/asm-sh/ide.h | 21 --------------------- include/asm-x86/ide.h | 18 ------------------ include/asm-xtensa/ide.h | 21 --------------------- include/linux/ide.h | 6 ++++++ 9 files changed, 6 insertions(+), 171 deletions(-) delete mode 100644 include/asm-alpha/ide.h delete mode 100644 include/asm-blackfin/ide.h delete mode 100644 include/asm-h8300/ide.h delete mode 100644 include/asm-ia64/ide.h delete mode 100644 include/asm-m32r/ide.h delete mode 100644 include/asm-sh/ide.h delete mode 100644 include/asm-x86/ide.h delete mode 100644 include/asm-xtensa/ide.h (limited to 'include/linux') diff --git a/include/asm-alpha/ide.h b/include/asm-alpha/ide.h deleted file mode 100644 index 55f9f6870249..000000000000 --- a/include/asm-alpha/ide.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * linux/include/asm-alpha/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the alpha architecture specific IDE code. - */ - -#ifndef __ASMalpha_IDE_H -#define __ASMalpha_IDE_H - -#ifdef __KERNEL__ - -#include - -#endif /* __KERNEL__ */ - -#endif /* __ASMalpha_IDE_H */ diff --git a/include/asm-blackfin/ide.h b/include/asm-blackfin/ide.h deleted file mode 100644 index 90bc50bd22e9..000000000000 --- a/include/asm-blackfin/ide.h +++ /dev/null @@ -1,25 +0,0 @@ -/****************************************************************************/ - -/* - * linux/include/asm-blackfin/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - * Copyright (C) 2001 Lineo Inc., davidm@snapgear.com - * Copyright (C) 2002 Greg Ungerer (gerg@snapgear.com) - * Copyright (C) 2002 Yoshinori Sato (ysato@users.sourceforge.jp) - * Copyright (C) 2005 Hennerich Michael (hennerich@blackfin.uclinux.org) - */ - -/****************************************************************************/ -#ifndef _BLACKFIN_IDE_H -#define _BLACKFIN_IDE_H -/****************************************************************************/ -#ifdef __KERNEL__ -/****************************************************************************/ - -#include - -/****************************************************************************/ -#endif /* __KERNEL__ */ -#endif /* _BLACKFIN_IDE_H */ -/****************************************************************************/ diff --git a/include/asm-h8300/ide.h b/include/asm-h8300/ide.h deleted file mode 100644 index 8f79ba2ff929..000000000000 --- a/include/asm-h8300/ide.h +++ /dev/null @@ -1,24 +0,0 @@ -/****************************************************************************/ - -/* - * linux/include/asm-h8300/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - * Copyright (C) 2001 Lineo Inc., davidm@snapgear.com - * Copyright (C) 2002 Greg Ungerer (gerg@snapgear.com) - * Copyright (C) 2002 Yoshinori Sato (ysato@users.sourceforge.jp) - */ - -/****************************************************************************/ -#ifndef _H8300_IDE_H -#define _H8300_IDE_H -/****************************************************************************/ -#ifdef __KERNEL__ -/****************************************************************************/ - -#include - -/****************************************************************************/ -#endif /* __KERNEL__ */ -#endif /* _H8300_IDE_H */ -/****************************************************************************/ diff --git a/include/asm-ia64/ide.h b/include/asm-ia64/ide.h deleted file mode 100644 index 5a0aedea4764..000000000000 --- a/include/asm-ia64/ide.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * linux/include/asm-ia64/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the ia64 architecture specific IDE code. - */ - -#ifndef __ASM_IA64_IDE_H -#define __ASM_IA64_IDE_H - -#ifdef __KERNEL__ - -#include - -#include - -#endif /* __KERNEL__ */ - -#endif /* __ASM_IA64_IDE_H */ diff --git a/include/asm-m32r/ide.h b/include/asm-m32r/ide.h deleted file mode 100644 index 0f1ec6973879..000000000000 --- a/include/asm-m32r/ide.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _ASM_M32R_IDE_H -#define _ASM_M32R_IDE_H - -/* - * linux/include/asm-m32r/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the i386 architecture specific IDE code. - */ - -#ifdef __KERNEL__ - -#include - -#endif /* __KERNEL__ */ - -#endif /* _ASM_M32R_IDE_H */ diff --git a/include/asm-sh/ide.h b/include/asm-sh/ide.h deleted file mode 100644 index 58e0bdd52be4..000000000000 --- a/include/asm-sh/ide.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * linux/include/asm-sh/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the i386 architecture specific IDE code. - * In future, SuperH code. - */ - -#ifndef __ASM_SH_IDE_H -#define __ASM_SH_IDE_H - -#ifdef __KERNEL__ - -#include - -#endif /* __KERNEL__ */ - -#endif /* __ASM_SH_IDE_H */ diff --git a/include/asm-x86/ide.h b/include/asm-x86/ide.h deleted file mode 100644 index 0289baf9ce0a..000000000000 --- a/include/asm-x86/ide.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the i386 architecture specific IDE code. - */ - -#ifndef __ASMi386_IDE_H -#define __ASMi386_IDE_H - -#ifdef __KERNEL__ - -#include - -#endif /* __KERNEL__ */ - -#endif /* __ASMi386_IDE_H */ diff --git a/include/asm-xtensa/ide.h b/include/asm-xtensa/ide.h deleted file mode 100644 index 18342a2cc774..000000000000 --- a/include/asm-xtensa/ide.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * include/asm-xtensa/ide.h - * - * 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 - * for more details. - * - * Copyright (C) 1994 - 1996 Linus Torvalds & authors - * Copyright (C) 2001 - 2005 Tensilica Inc. - */ - -#ifndef _XTENSA_IDE_H -#define _XTENSA_IDE_H - -#ifdef __KERNEL__ - -#include - -#endif /* __KERNEL__ */ - -#endif /* _XTENSA_IDE_H */ diff --git a/include/linux/ide.h b/include/linux/ide.h index 76fe00b24b51..fd78b401b036 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -211,7 +211,13 @@ static inline int __ide_default_irq(unsigned long base) return 0; } +#if defined(CONFIG_ARM) || defined(CONFIG_FRV) || defined(CONFIG_M68K) || \ + defined(CONFIG_MIPS) || defined(CONFIG_MN10300) || defined(CONFIG_PARISC) \ + || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || defined(CONFIG_SPARC64) #include +#else +#include +#endif #ifndef MAX_HWIFS #if defined(CONFIG_BLACKFIN) || defined(CONFIG_H8300) || defined(CONFIG_XTENSA) -- cgit From a326b02b0c576001353dbc489154959b0889c6bf Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 24 Jul 2008 22:53:33 +0200 Subject: ide: drop 'name' parameter from ->init_chipset method There should be no functional changes caused by this patch. Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/pci/aec62xx.c | 2 +- drivers/ide/pci/alim15x3.c | 5 ++--- drivers/ide/pci/amd74xx.c | 19 ++++++++----------- drivers/ide/pci/cmd64x.c | 2 +- drivers/ide/pci/cs5530.c | 7 +++---- drivers/ide/pci/cy82c693.c | 10 +++++----- drivers/ide/pci/hpt34x.c | 2 +- drivers/ide/pci/hpt366.c | 3 ++- drivers/ide/pci/it821x.c | 2 +- drivers/ide/pci/pdc202xx_new.c | 3 ++- drivers/ide/pci/pdc202xx_old.c | 3 +-- drivers/ide/pci/piix.c | 3 +-- drivers/ide/pci/serverworks.c | 6 +++--- drivers/ide/pci/siimage.c | 8 +++----- drivers/ide/pci/sis5513.c | 3 +-- drivers/ide/pci/sl82c105.c | 2 +- drivers/ide/pci/via82cxxx.c | 3 +-- drivers/ide/setup-pci.c | 2 +- include/linux/ide.h | 2 +- 19 files changed, 39 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index f6dc6c20f3af..e0c8fe7d9fea 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -140,7 +140,7 @@ static void aec_set_pio_mode(ide_drive_t *drive, const u8 pio) drive->hwif->port_ops->set_dma_mode(drive, pio + XFER_PIO_0); } -static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev) { /* These are necessary to get AEC6280 Macintosh cards to work */ if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) || diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index a099c4dd599d..b582687e0cd4 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -209,13 +209,12 @@ static int ali15x3_dma_setup(ide_drive_t *drive) /** * init_chipset_ali15x3 - Initialise an ALi IDE controller * @dev: PCI device - * @name: Name of the controller * * This function initializes the ALI IDE controller and where * appropriate also sets up the 1533 southbridge. */ - -static unsigned int __devinit init_chipset_ali15x3 (struct pci_dev *dev, const char *name) + +static unsigned int __devinit init_chipset_ali15x3(struct pci_dev *dev) { unsigned long flags; u8 tmpbyte; diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index cbf78edfe00b..2cea7bf51a0f 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -112,15 +112,13 @@ static void amd_set_pio_mode(ide_drive_t *drive, const u8 pio) amd_set_drive(drive, XFER_PIO_0 + pio); } -static void __devinit amd7409_cable_detect(struct pci_dev *dev, - const char *name) +static void __devinit amd7409_cable_detect(struct pci_dev *dev) { /* no host side cable detection */ amd_80w = 0x03; } -static void __devinit amd7411_cable_detect(struct pci_dev *dev, - const char *name) +static void __devinit amd7411_cable_detect(struct pci_dev *dev) { int i; u32 u = 0; @@ -131,9 +129,9 @@ static void __devinit amd7411_cable_detect(struct pci_dev *dev, amd_80w = ((t & 0x3) ? 1 : 0) | ((t & 0xc) ? 2 : 0); for (i = 24; i >= 0; i -= 8) if (((u >> i) & 4) && !(amd_80w & (1 << (1 - (i >> 4))))) { - printk(KERN_WARNING "%s %s: BIOS didn't set cable bits " - "correctly. Enabling workaround.\n", - name, pci_name(dev)); + printk(KERN_WARNING DRV_NAME " %s: BIOS didn't set " + "cable bits correctly. Enabling workaround.\n", + pci_name(dev)); amd_80w |= (1 << (1 - (i >> 4))); } } @@ -142,8 +140,7 @@ static void __devinit amd7411_cable_detect(struct pci_dev *dev, * The initialization callback. Initialize drive independent registers. */ -static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev) { u8 t = 0, offset = amd_offset(dev); @@ -156,9 +153,9 @@ static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev, ; /* no UDMA > 2 */ else if (dev->vendor == PCI_VENDOR_ID_AMD && dev->device == PCI_DEVICE_ID_AMD_VIPER_7409) - amd7409_cable_detect(dev, name); + amd7409_cable_detect(dev); else - amd7411_cable_detect(dev, name); + amd7411_cable_detect(dev); /* * Take care of prefetch & postwrite. diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index 3d84debaf81f..1360b4fa9fd3 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -332,7 +332,7 @@ static int cmd646_1_dma_end(ide_drive_t *drive) return (dma_stat & 7) != 4; } -static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev) { u8 mrdmode = 0; diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index 5543c8677a5a..f235db8c678b 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -129,12 +129,11 @@ static void cs5530_set_dma_mode(ide_drive_t *drive, const u8 mode) /** * init_chipset_5530 - set up 5530 bridge * @dev: PCI device - * @name: device name * * Initialize the cs5530 bridge for reliable IDE DMA operation. */ -static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_cs5530(struct pci_dev *dev) { struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; @@ -153,11 +152,11 @@ static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const ch } } if (!master_0) { - printk(KERN_ERR "%s: unable to locate PCI MASTER function\n", name); + printk(KERN_ERR DRV_NAME ": unable to locate PCI MASTER function\n"); goto out; } if (!cs5530_0) { - printk(KERN_ERR "%s: unable to locate CS5530 LEGACY function\n", name); + printk(KERN_ERR DRV_NAME ": unable to locate CS5530 LEGACY function\n"); goto out; } diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c index 41c7f3351eb6..bfae2f882f48 100644 --- a/drivers/ide/pci/cy82c693.c +++ b/drivers/ide/pci/cy82c693.c @@ -332,7 +332,7 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio) /* * this function is called during init and is used to setup the cy82c693 chip */ -static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev) { if (PCI_FUNC(dev->devfn) != 1) return 0; @@ -351,8 +351,8 @@ static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const c data = inb(CY82_DATA_PORT); #if CY82C693_DEBUG_INFO - printk(KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n", - name, data); + printk(KERN_INFO DRV_NAME ": Peripheral Configuration Register: 0x%X\n", + data); #endif /* CY82C693_DEBUG_INFO */ /* @@ -373,8 +373,8 @@ static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const c outb(data, CY82_DATA_PORT); #if CY82C693_DEBUG_INFO - printk(KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n", - name, data); + printk(KERN_INFO ": New Peripheral Configuration Register: 0x%X\n", + data); #endif /* CY82C693_DEBUG_INFO */ #endif /* CY82C693_SETDMA_CLOCK */ diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index baabb4ce0d78..6009b0b9655d 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -79,7 +79,7 @@ static void hpt34x_set_pio_mode(ide_drive_t *drive, const u8 pio) */ #define HPT34X_PCI_INIT_REG 0x80 -static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev) { int i = 0; unsigned long hpt34xIoBase = pci_resource_start(dev, 4); diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 6a1c65c3be3e..5271b246b88c 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -970,11 +970,12 @@ static int __devinit hpt37x_calibrate_dpll(struct pci_dev *dev, u16 f_low, u16 f return 1; } -static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev) { unsigned long io_base = pci_resource_start(dev, 4); struct ide_host *host = pci_get_drvdata(dev); struct hpt_info *info = host->host_priv + (&dev->dev == host->dev[1]); + const char *name = DRV_NAME; u8 pci_clk, dpll_clk = 0; /* PCI and DPLL clock in MHz */ u8 chip_type; enum ata_clock clock; diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index 74173352741f..e16a1d113a2a 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -605,7 +605,7 @@ static void __devinit it8212_disable_raid(struct pci_dev *dev) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20); } -static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev) { u8 conf; static char *mode[2] = { "pass through", "smart" }; diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c index 1f6791957227..998615fa285f 100644 --- a/drivers/ide/pci/pdc202xx_new.c +++ b/drivers/ide/pci/pdc202xx_new.c @@ -326,8 +326,9 @@ static void __devinit apple_kiwi_init(struct pci_dev *pdev) } #endif /* CONFIG_PPC_PMAC */ -static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev) { + const char *name = DRV_NAME; unsigned long dma_base = pci_resource_start(dev, 4); unsigned long sec_dma_base = dma_base + 0x08; long pll_input, pll_output, ratio; diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index da92d127868f..6ff2def58da0 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -265,8 +265,7 @@ static void pdc202xx_dma_timeout(ide_drive_t *drive) ide_dma_timeout(drive); } -static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev) { unsigned long dmabase = pci_resource_start(dev, 4); u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0; diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index 9eb411f5c358..7fc3022dcf68 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -200,13 +200,12 @@ static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed) /** * init_chipset_ich - set up the ICH chipset * @dev: PCI device to set up - * @name: Name of the device * * Initialize the PCI device as required. For the ICH this turns * out to be nice and simple. */ -static unsigned int __devinit init_chipset_ich(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_ich(struct pci_dev *dev) { u32 extra = 0; diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index e26bc8326dbb..d173f2937722 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -174,7 +174,7 @@ static void svwks_set_dma_mode(ide_drive_t *drive, const u8 speed) pci_write_config_byte(dev, 0x54, ultra_enable); } -static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_svwks(struct pci_dev *dev) { unsigned int reg; u8 btr; @@ -190,8 +190,8 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha pci_read_config_dword(isa_dev, 0x64, ®); reg &= ~0x00002000; /* disable 600ns interrupt mask */ if(!(reg & 0x00004000)) - printk(KERN_DEBUG "%s %s: UDMA not BIOS " - "enabled.\n", name, pci_name(dev)); + printk(KERN_DEBUG DRV_NAME " %s: UDMA not BIOS " + "enabled.\n", pci_name(dev)); reg |= 0x00004000; /* enable UDMA/33 support */ pci_write_config_dword(isa_dev, 0x64, reg); } diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 572b479a3922..b8ad9ad6cf0d 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -457,14 +457,12 @@ static void sil_sata_pre_reset(ide_drive_t *drive) /** * init_chipset_siimage - set up an SI device * @dev: PCI device - * @name: device name * * Perform the initial PCI set up for this device. Attempt to switch * to 133 MHz clocking if the system isn't already set up to do it. */ -static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev) { struct ide_host *host = pci_get_drvdata(dev); void __iomem *ioaddr = host->host_priv; @@ -541,8 +539,8 @@ static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev, { "== 100", "== 133", "== 2X PCI", "DISABLED!" }; tmp >>= 4; - printk(KERN_INFO "%s %s: BASE CLOCK %s\n", - name, pci_name(dev), clk_str[tmp & 3]); + printk(KERN_INFO DRV_NAME " %s: BASE CLOCK %s\n", + pci_name(dev), clk_str[tmp & 3]); } return 0; diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c index 6fcb46c87871..cc95f90b53b7 100644 --- a/drivers/ide/pci/sis5513.c +++ b/drivers/ide/pci/sis5513.c @@ -448,8 +448,7 @@ static int __devinit sis_find_family(struct pci_dev *dev) return chipset_family; } -static unsigned int __devinit init_chipset_sis5513(struct pci_dev *dev, - const char *name) +static unsigned int __devinit init_chipset_sis5513(struct pci_dev *dev) { /* Make general config ops here 1/ tell IDE channels to operate in Compatibility mode only diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index fa720db3de10..73905bcc08fb 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -272,7 +272,7 @@ static u8 sl82c105_bridge_revision(struct pci_dev *dev) * channel 0 here at least, but channel 1 has to be enabled by * firmware or arch code. We still set both to 16 bits mode. */ -static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev, const char *msg) +static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev) { u32 val; diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index 170e058f1fbd..454d2bf62dce 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -262,13 +262,12 @@ static void __devinit via_cable_detect(struct via82cxxx_dev *vdev, u32 u) /** * init_chipset_via82cxxx - initialization handler * @dev: PCI device - * @name: Name of interface * * The initialization callback. Here we determine the IDE chip type * and initialize its drive independent registers. */ -static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const char *name) +static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev) { struct ide_host *host = pci_get_drvdata(dev); struct via82cxxx_dev *vdev = host->host_priv; diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index d9655aeb013b..a8e9e8a69a52 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -515,7 +515,7 @@ static int do_ide_setup_pci_device(struct pci_dev *dev, * space, place chipset into init-mode, and/or preserve * an interrupt if the card is not native ide support. */ - ret = d->init_chipset ? d->init_chipset(dev, d->name) : 0; + ret = d->init_chipset ? d->init_chipset(dev) : 0; if (ret < 0) goto out; diff --git a/include/linux/ide.h b/include/linux/ide.h index fd78b401b036..b846bc44a27e 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1206,7 +1206,7 @@ enum { struct ide_port_info { char *name; - unsigned int (*init_chipset)(struct pci_dev *, const char *); + unsigned int (*init_chipset)(struct pci_dev *); void (*init_iops)(ide_hwif_t *); void (*init_hwif)(ide_hwif_t *); int (*init_dma)(ide_hwif_t *, -- cgit From 674bfc23c585b34c42263d73fb51710d49762a23 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Jul 2008 12:06:03 -0500 Subject: virtio: clarify that ABI is usable by any implementations We want others to implement and use virtio, so it makes sense to BSD license the non-__KERNEL__ parts of the headers to make this crystal clear. Signed-off-by: Rusty Russell Acked-by: Christian Borntraeger Acked-by: Mark McLoughlin Acked-by: Ryan Harper Acked-by: Eric Van Hensbergen Acked-by: Anthony Liguori --- include/linux/virtio_9p.h | 2 ++ include/linux/virtio_balloon.h | 2 ++ include/linux/virtio_blk.h | 2 ++ include/linux/virtio_config.h | 3 +++ include/linux/virtio_console.h | 2 ++ include/linux/virtio_net.h | 2 ++ include/linux/virtio_pci.h | 5 ++--- include/linux/virtio_rng.h | 2 ++ 8 files changed, 17 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/virtio_9p.h b/include/linux/virtio_9p.h index 8eff0b53910b..b3c4a60ceeb3 100644 --- a/include/linux/virtio_9p.h +++ b/include/linux/virtio_9p.h @@ -1,5 +1,7 @@ #ifndef _LINUX_VIRTIO_9P_H #define _LINUX_VIRTIO_9P_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #include /* The ID for virtio console */ diff --git a/include/linux/virtio_balloon.h b/include/linux/virtio_balloon.h index 979524ee75b7..c30c7bfbf39b 100644 --- a/include/linux/virtio_balloon.h +++ b/include/linux/virtio_balloon.h @@ -1,5 +1,7 @@ #ifndef _LINUX_VIRTIO_BALLOON_H #define _LINUX_VIRTIO_BALLOON_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #include /* The ID for virtio_balloon */ diff --git a/include/linux/virtio_blk.h b/include/linux/virtio_blk.h index 5f79a5f9de79..6a66c7f30bcb 100644 --- a/include/linux/virtio_blk.h +++ b/include/linux/virtio_blk.h @@ -1,5 +1,7 @@ #ifndef _LINUX_VIRTIO_BLK_H #define _LINUX_VIRTIO_BLK_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #include /* The ID for virtio_block */ diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index f364bbf63c34..7eb4b34d13bb 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -1,5 +1,8 @@ #ifndef _LINUX_VIRTIO_CONFIG_H #define _LINUX_VIRTIO_CONFIG_H +/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so + * anyone can use the definitions to implement compatible drivers/servers. */ + /* Virtio devices use a standardized configuration space to define their * features and pass configuration information, but each implementation can * store and access that space differently. */ diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index ed2d4ead7eb7..19a0da0dba41 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -1,6 +1,8 @@ #ifndef _LINUX_VIRTIO_CONSOLE_H #define _LINUX_VIRTIO_CONSOLE_H #include +/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so + * anyone can use the definitions to implement compatible drivers/servers. */ /* The ID for virtio console */ #define VIRTIO_ID_CONSOLE 3 diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 38c0571820fb..5e33761b9b8a 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -1,5 +1,7 @@ #ifndef _LINUX_VIRTIO_NET_H #define _LINUX_VIRTIO_NET_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #include /* The ID for virtio_net */ diff --git a/include/linux/virtio_pci.h b/include/linux/virtio_pci.h index b3151659cf49..cdef35742932 100644 --- a/include/linux/virtio_pci.h +++ b/include/linux/virtio_pci.h @@ -9,9 +9,8 @@ * Authors: * Anthony Liguori * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #ifndef _LINUX_VIRTIO_PCI_H diff --git a/include/linux/virtio_rng.h b/include/linux/virtio_rng.h index 331afb6c9f62..1a85dab8a940 100644 --- a/include/linux/virtio_rng.h +++ b/include/linux/virtio_rng.h @@ -1,5 +1,7 @@ #ifndef _LINUX_VIRTIO_RNG_H #define _LINUX_VIRTIO_RNG_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. */ #include /* The ID for virtio_rng */ -- cgit From 066f4d82a67f621ddd547bfa4b9c94631d8457b0 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Thu, 29 May 2008 11:08:26 +0200 Subject: virtio_blk: check for hardsector size from host Currently virtio_blk assumes a 512 byte hard sector size. This can cause trouble / performance issues if the backing has a different block size (like a file on an ext3 file system formatted with 4k block size or a dasd). Lets add a feature flag that tells the guest to use a different hard sector size than 512 byte. Signed-off-by: Christian Borntraeger Signed-off-by: Rusty Russell --- drivers/block/virtio_blk.c | 10 +++++++++- include/linux/virtio_blk.h | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index dd7ea203f940..42251095134f 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -196,6 +196,7 @@ static int virtblk_probe(struct virtio_device *vdev) int err; u64 cap; u32 v; + u32 blk_size; if (index_to_minor(index) >= 1 << MINORBITS) return -ENOSPC; @@ -290,6 +291,13 @@ static int virtblk_probe(struct virtio_device *vdev) if (!err) blk_queue_max_hw_segments(vblk->disk->queue, v); + /* Host can optionally specify the block size of the device */ + err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE, + offsetof(struct virtio_blk_config, blk_size), + &blk_size); + if (!err) + blk_queue_hardsect_size(vblk->disk->queue, blk_size); + add_disk(vblk->disk); return 0; @@ -330,7 +338,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, - VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, + VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, }; static struct virtio_driver virtio_blk = { diff --git a/include/linux/virtio_blk.h b/include/linux/virtio_blk.h index 6a66c7f30bcb..c1aef85243bf 100644 --- a/include/linux/virtio_blk.h +++ b/include/linux/virtio_blk.h @@ -13,6 +13,7 @@ #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ #define VIRTIO_BLK_F_GEOMETRY 4 /* Legacy geometry available */ #define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ +#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ struct virtio_blk_config { @@ -28,6 +29,8 @@ struct virtio_blk_config __u8 heads; __u8 sectors; } geometry; + /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ + __u32 blk_size; } __attribute__((packed)); /* These two define direction. */ -- cgit From dd7c7bc46211785a1aa7d70feb15830f62682b3c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Jul 2008 12:06:07 -0500 Subject: virtio: Formally reserve bits 28-31 to be 'transport' features. We assign feature bits as required, but it makes sense to reserve some for the particular transport, rather than the particular device. Signed-off-by: Rusty Russell --- drivers/virtio/virtio.c | 5 +++++ include/linux/virtio_config.h | 6 ++++++ 2 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index fc85cba64578..baf103361e3a 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -113,6 +113,11 @@ static int virtio_dev_probe(struct device *_d) set_bit(f, dev->features); } + /* Transport features are always preserved to pass to set_features. */ + for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) + if (device_features & (1 << i)) + set_bit(i, dev->features); + err = drv->probe(dev); if (err) add_status(dev, VIRTIO_CONFIG_S_FAILED); diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 7eb4b34d13bb..5a30cfb7934b 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -18,6 +18,12 @@ /* We've given up on this device. */ #define VIRTIO_CONFIG_S_FAILED 0x80 +/* Some virtio feature bits (currently bits 28 through 31) are reserved for the + * transport being used (eg. virtio_ring), the rest are per-device feature + * bits. */ +#define VIRTIO_TRANSPORT_F_START 28 +#define VIRTIO_TRANSPORT_F_END 32 + /* Do we get callbacks when the ring is completely used, even if we've * suppressed them? */ #define VIRTIO_F_NOTIFY_ON_EMPTY 24 -- cgit From c624896e488ba2bff5ae497782cfb265c8b00646 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Jul 2008 12:06:07 -0500 Subject: virtio: Rename set_features to finalize_features Rather than explicitly handing the features to the lower-level, we just hand the virtio_device and have it set the features. This make it clear that it has the chance to manipulate the features of the device at this point (and that all feature negotiation is already done). Signed-off-by: Rusty Russell --- drivers/lguest/lguest_device.c | 11 ++++++----- drivers/s390/kvm/kvm_virtio.c | 11 ++++++----- drivers/virtio/virtio.c | 5 ++--- drivers/virtio/virtio_pci.c | 10 ++++++---- include/linux/virtio_config.h | 7 ++++--- 5 files changed, 24 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 1a8de57289eb..54fdc2aa4806 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -98,16 +98,17 @@ static u32 lg_get_features(struct virtio_device *vdev) return features; } -static void lg_set_features(struct virtio_device *vdev, u32 features) +static void lg_finalize_features(struct virtio_device *vdev) { - unsigned int i; + unsigned int i, bits; struct lguest_device_desc *desc = to_lgdev(vdev)->desc; /* Second half of bitmap is features we accept. */ u8 *out_features = lg_features(desc) + desc->feature_len; memset(out_features, 0, desc->feature_len); - for (i = 0; i < min(desc->feature_len * 8, 32); i++) { - if (features & (1 << i)) + bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) { + if (test_bit(i, vdev->features)) out_features[i / 8] |= (1 << (i % 8)); } } @@ -297,7 +298,7 @@ static void lg_del_vq(struct virtqueue *vq) /* The ops structure which hooks everything together. */ static struct virtio_config_ops lguest_config_ops = { .get_features = lg_get_features, - .set_features = lg_set_features, + .finalize_features = lg_finalize_features, .get = lg_get, .set = lg_set, .get_status = lg_get_status, diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index d41f234bb2c2..5953510e7d5f 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -88,16 +88,17 @@ static u32 kvm_get_features(struct virtio_device *vdev) return features; } -static void kvm_set_features(struct virtio_device *vdev, u32 features) +static void kvm_finalize_features(struct virtio_device *vdev) { - unsigned int i; + unsigned int i, bits; struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; /* Second half of bitmap is features we accept. */ u8 *out_features = kvm_vq_features(desc) + desc->feature_len; memset(out_features, 0, desc->feature_len); - for (i = 0; i < min(desc->feature_len * 8, 32); i++) { - if (features & (1 << i)) + bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) { + if (test_bit(i, vdev->features)) out_features[i / 8] |= (1 << (i % 8)); } } @@ -223,7 +224,7 @@ static void kvm_del_vq(struct virtqueue *vq) */ static struct virtio_config_ops kvm_vq_configspace_ops = { .get_features = kvm_get_features, - .set_features = kvm_set_features, + .finalize_features = kvm_finalize_features, .get = kvm_get, .set = kvm_set, .get_status = kvm_get_status, diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index baf103361e3a..5b78fd0aff0a 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -113,7 +113,7 @@ static int virtio_dev_probe(struct device *_d) set_bit(f, dev->features); } - /* Transport features are always preserved to pass to set_features. */ + /* Transport features always preserved to pass to finalize_features. */ for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) if (device_features & (1 << i)) set_bit(i, dev->features); @@ -122,8 +122,7 @@ static int virtio_dev_probe(struct device *_d) if (err) add_status(dev, VIRTIO_CONFIG_S_FAILED); else { - /* They should never have set feature bits beyond 32 */ - dev->config->set_features(dev, dev->features[0]); + dev->config->finalize_features(dev); add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); } return err; diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index eae7236310e4..9855975a72a3 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -94,12 +94,14 @@ static u32 vp_get_features(struct virtio_device *vdev) return ioread32(vp_dev->ioaddr + VIRTIO_PCI_HOST_FEATURES); } -/* virtio config->set_features() implementation */ -static void vp_set_features(struct virtio_device *vdev, u32 features) +/* virtio config->finalize_features() implementation */ +static void vp_finalize_features(struct virtio_device *vdev) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); - iowrite32(features, vp_dev->ioaddr + VIRTIO_PCI_GUEST_FEATURES); + /* We only support 32 feature bits. */ + BUILD_BUG_ON(ARRAY_SIZE(vdev->features) != 1); + iowrite32(vdev->features[0], vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES); } /* virtio config->get() implementation */ @@ -297,7 +299,7 @@ static struct virtio_config_ops virtio_pci_config_ops = { .find_vq = vp_find_vq, .del_vq = vp_del_vq, .get_features = vp_get_features, - .set_features = vp_set_features, + .finalize_features = vp_finalize_features, }; /* the PCI probing function */ diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 5a30cfb7934b..bf8ec283b232 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -61,9 +61,10 @@ * @get_features: get the array of feature bits for this device. * vdev: the virtio_device * Returns the first 32 feature bits (all we currently need). - * @set_features: confirm what device features we'll be using. + * @finalize_features: confirm what device features we'll be using. * vdev: the virtio_device - * feature: the first 32 feature bits + * This gives the final feature bits for the device: it can change + * the dev->feature bits if it wants. */ struct virtio_config_ops { @@ -79,7 +80,7 @@ struct virtio_config_ops void (*callback)(struct virtqueue *)); void (*del_vq)(struct virtqueue *vq); u32 (*get_features)(struct virtio_device *vdev); - void (*set_features)(struct virtio_device *vdev, u32 features); + void (*finalize_features)(struct virtio_device *vdev); }; /* If driver didn't advertise the feature, it will never appear. */ -- cgit From e34f87256794b87e7f4a8f1812538be7b7b5214c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Jul 2008 12:06:13 -0500 Subject: virtio: Add transport feature handling stub for virtio_ring. To prepare for virtio_ring transport feature bits, hook in a call in all the users to manipulate them. This currently just clears all the bits, since it doesn't understand any features. Signed-off-by: Rusty Russell --- drivers/lguest/lguest_device.c | 3 +++ drivers/s390/kvm/kvm_virtio.c | 3 +++ drivers/virtio/virtio_pci.c | 3 +++ drivers/virtio/virtio_ring.c | 16 ++++++++++++++++ include/linux/virtio_ring.h | 2 ++ 5 files changed, 27 insertions(+) (limited to 'include/linux') diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 54fdc2aa4806..37344aaee22f 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -105,6 +105,9 @@ static void lg_finalize_features(struct virtio_device *vdev) /* Second half of bitmap is features we accept. */ u8 *out_features = lg_features(desc) + desc->feature_len; + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + memset(out_features, 0, desc->feature_len); bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; for (i = 0; i < bits; i++) { diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 5953510e7d5f..79954bd6bfa5 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -95,6 +95,9 @@ static void kvm_finalize_features(struct virtio_device *vdev) /* Second half of bitmap is features we accept. */ u8 *out_features = kvm_vq_features(desc) + desc->feature_len; + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + memset(out_features, 0, desc->feature_len); bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; for (i = 0; i < bits; i++) { diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 9855975a72a3..c7dc37c7cce9 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -99,6 +99,9 @@ static void vp_finalize_features(struct virtio_device *vdev) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + /* We only support 32 feature bits. */ BUILD_BUG_ON(ARRAY_SIZE(vdev->features) != 1); iowrite32(vdev->features[0], vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES); diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 21d9a62767af..6eb5303fed11 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -18,6 +18,7 @@ */ #include #include +#include #include #ifdef DEBUG @@ -323,4 +324,19 @@ void vring_del_virtqueue(struct virtqueue *vq) } EXPORT_SYMBOL_GPL(vring_del_virtqueue); +/* Manipulates transport-specific feature bits. */ +void vring_transport_features(struct virtio_device *vdev) +{ + unsigned int i; + + for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) { + switch (i) { + default: + /* We don't understand this bit. */ + clear_bit(i, vdev->features); + } + } +} +EXPORT_SYMBOL_GPL(vring_transport_features); + MODULE_LICENSE("GPL"); diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h index abe481ed990e..c4a598fb3826 100644 --- a/include/linux/virtio_ring.h +++ b/include/linux/virtio_ring.h @@ -120,6 +120,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int num, void (*notify)(struct virtqueue *vq), void (*callback)(struct virtqueue *vq)); void vring_del_virtqueue(struct virtqueue *vq); +/* Filter out transport-specific feature bits. */ +void vring_transport_features(struct virtio_device *vdev); irqreturn_t vring_interrupt(int irq, void *_vq); #endif /* __KERNEL__ */ -- cgit From ed9559d38a87a44e3bda87d73a50aab92471d7dc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Jul 2008 12:11:09 +1000 Subject: Label kthread_create() with printf attribute tag. Obvious misc patch been in my queue (& linux-next) for over a cycle. Signed-off-by: Rusty Russell Signed-off-by: Linus Torvalds --- include/linux/kthread.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 00dd957e245b..aabc8a13ba71 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -6,7 +6,8 @@ struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, - const char namefmt[], ...); + const char namefmt[], ...) + __attribute__((format(printf, 3, 4))); /** * kthread_run - create and wake a thread. -- cgit From 483fad1c3fa1060d7e6710e84a065ad514571739 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Tue, 22 Jul 2008 04:48:46 +1000 Subject: ELF loader support for auxvec base platform string Some IBM POWER-based platforms have the ability to run in a mode which mostly appears to the OS as a different processor from the actual hardware. For example, a Power6 system may appear to be a Power5+, which makes the AT_PLATFORM value "power5+". This means that programs are restricted to the ISA supported by Power5+; Power6-specific instructions are treated as illegal. However, some applications (virtual machines, optimized libraries) can benefit from knowledge of the underlying CPU model. A new aux vector entry, AT_BASE_PLATFORM, will denote the actual hardware. For example, on a Power6 system in Power5+ compatibility mode, AT_PLATFORM will be "power5+" and AT_BASE_PLATFORM will be "power6". The idea is that AT_PLATFORM indicates the instruction set supported, while AT_BASE_PLATFORM indicates the underlying microarchitecture. If the architecture has defined ELF_BASE_PLATFORM, copy that value to the user stack in the same manner as ELF_PLATFORM. Signed-off-by: Nathan Lynch Acked-by: Andrew Morton Signed-off-by: Benjamin Herrenschmidt --- fs/binfmt_elf.c | 28 ++++++++++++++++++++++++++++ include/linux/auxvec.h | 6 +++++- 2 files changed, 33 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 639d2d8b5710..742c8f530481 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -131,6 +131,15 @@ static int padzero(unsigned long elf_bss) #define STACK_ALLOC(sp, len) ({ sp -= len ; sp; }) #endif +#ifndef ELF_BASE_PLATFORM +/* + * AT_BASE_PLATFORM indicates the "real" hardware/microarchitecture. + * If the arch defines ELF_BASE_PLATFORM (in asm/elf.h), the value + * will be copied to the user stack in the same manner as AT_PLATFORM. + */ +#define ELF_BASE_PLATFORM NULL +#endif + static int create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, unsigned long load_addr, unsigned long interp_load_addr) @@ -142,7 +151,9 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, elf_addr_t __user *envp; elf_addr_t __user *sp; elf_addr_t __user *u_platform; + elf_addr_t __user *u_base_platform; const char *k_platform = ELF_PLATFORM; + const char *k_base_platform = ELF_BASE_PLATFORM; int items; elf_addr_t *elf_info; int ei_index = 0; @@ -172,6 +183,19 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, return -EFAULT; } + /* + * If this architecture has a "base" platform capability + * string, copy it to userspace. + */ + u_base_platform = NULL; + if (k_base_platform) { + size_t len = strlen(k_base_platform) + 1; + + u_base_platform = (elf_addr_t __user *)STACK_ALLOC(p, len); + if (__copy_to_user(u_base_platform, k_base_platform, len)) + return -EFAULT; + } + /* Create the ELF interpreter info */ elf_info = (elf_addr_t *)current->mm->saved_auxv; /* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */ @@ -209,6 +233,10 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, NEW_AUX_ENT(AT_PLATFORM, (elf_addr_t)(unsigned long)u_platform); } + if (k_base_platform) { + NEW_AUX_ENT(AT_BASE_PLATFORM, + (elf_addr_t)(unsigned long)u_base_platform); + } if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) { NEW_AUX_ENT(AT_EXECFD, bprm->interp_data); } diff --git a/include/linux/auxvec.h b/include/linux/auxvec.h index 0da17d14fd13..d7afa9dd6635 100644 --- a/include/linux/auxvec.h +++ b/include/linux/auxvec.h @@ -26,9 +26,13 @@ #define AT_SECURE 23 /* secure mode boolean */ +#define AT_BASE_PLATFORM 24 /* string identifying real platform, may + * differ from AT_PLATFORM. */ + #define AT_EXECFN 31 /* filename of program */ + #ifdef __KERNEL__ -#define AT_VECTOR_SIZE_BASE 17 /* NEW_AUX_ENT entries in auxiliary table */ +#define AT_VECTOR_SIZE_BASE 18 /* NEW_AUX_ENT entries in auxiliary table */ /* number of "#define AT_.*" above, minus {AT_NULL, AT_IGNORE, AT_NOTELF} */ #endif -- cgit From 95984f62c9b0bf6d89ef4f514b1afe73623481de Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 22 Jul 2008 18:41:10 +0200 Subject: firewire: fw-ohci: TSB43AB22/A dualbuffer workaround Isochronous reception in dualbuffer mode is reportedly broken with TI TSB43AB22A on x86-64. Descriptor addresses above 2G have been determined as the trigger: https://bugzilla.redhat.com/show_bug.cgi?id=435550 Two fixes are possible: - pci_set_consistent_dma_mask(pdev, DMA_31BIT_MASK); at least when IR descriptors are allocated, or - simply don't use dualbuffer. This fix implements the latter workaround. But we keep using dualbuffer on x86-32 which won't give us highmen (and thus physical addresses outside the 31bit range) in coherent DMA memory allocations. Right now we could for example also whitelist PPC32, but DMA mapping implementation details are expected to change there. Signed-off-by: Stefan Richter Signed-off-by: Jarod Wilson --- drivers/firewire/fw-ohci.c | 37 ++++++++++++++++++++++++------------- include/linux/pci_ids.h | 1 + 2 files changed, 25 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 333b12544dd1..a4eff32621b5 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -171,7 +171,6 @@ struct iso_context { struct fw_ohci { struct fw_card card; - u32 version; __iomem char *registers; dma_addr_t self_id_bus; __le32 *self_id_cpu; @@ -180,6 +179,8 @@ struct fw_ohci { int generation; int request_generation; /* for timestamping incoming requests */ u32 bus_seconds; + + bool use_dualbuffer; bool old_uninorth; bool bus_reset_packet_quirk; @@ -1885,7 +1886,7 @@ ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size) } else { mask = &ohci->ir_context_mask; list = ohci->ir_context_list; - if (ohci->version >= OHCI_VERSION_1_1) + if (ohci->use_dualbuffer) callback = handle_ir_dualbuffer_packet; else callback = handle_ir_packet_per_buffer; @@ -1949,7 +1950,7 @@ static int ohci_start_iso(struct fw_iso_context *base, } else { index = ctx - ohci->ir_context_list; control = IR_CONTEXT_ISOCH_HEADER; - if (ohci->version >= OHCI_VERSION_1_1) + if (ohci->use_dualbuffer) control |= IR_CONTEXT_DUAL_BUFFER_MODE; match = (tags << 28) | (sync << 8) | ctx->base.channel; if (cycle >= 0) { @@ -2279,7 +2280,7 @@ ohci_queue_iso(struct fw_iso_context *base, spin_lock_irqsave(&ctx->context.ohci->lock, flags); if (base->type == FW_ISO_CONTEXT_TRANSMIT) retval = ohci_queue_iso_transmit(base, packet, buffer, payload); - else if (ctx->context.ohci->version >= OHCI_VERSION_1_1) + else if (ctx->context.ohci->use_dualbuffer) retval = ohci_queue_iso_receive_dualbuffer(base, packet, buffer, payload); else @@ -2341,7 +2342,7 @@ static int __devinit pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) { struct fw_ohci *ohci; - u32 bus_options, max_receive, link_speed; + u32 bus_options, max_receive, link_speed, version; u64 guid; int err; size_t size; @@ -2366,12 +2367,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0); pci_set_drvdata(dev, ohci); -#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32) - ohci->old_uninorth = dev->vendor == PCI_VENDOR_ID_APPLE && - dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW; -#endif - ohci->bus_reset_packet_quirk = dev->vendor == PCI_VENDOR_ID_TI; - spin_lock_init(&ohci->lock); tasklet_init(&ohci->bus_reset_tasklet, @@ -2390,6 +2385,23 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) goto fail_iomem; } + version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; + ohci->use_dualbuffer = version >= OHCI_VERSION_1_1; + +/* x86-32 currently doesn't use highmem for dma_alloc_coherent */ +#if !defined(CONFIG_X86_32) + /* dual-buffer mode is broken with descriptor addresses above 2G */ + if (dev->vendor == PCI_VENDOR_ID_TI && + dev->device == PCI_DEVICE_ID_TI_TSB43AB22) + ohci->use_dualbuffer = false; +#endif + +#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32) + ohci->old_uninorth = dev->vendor == PCI_VENDOR_ID_APPLE && + dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW; +#endif + ohci->bus_reset_packet_quirk = dev->vendor == PCI_VENDOR_ID_TI; + ar_context_init(&ohci->ar_request_ctx, ohci, OHCI1394_AsReqRcvContextControlSet); @@ -2441,9 +2453,8 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) if (err < 0) goto fail_self_id; - ohci->version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; fw_notify("Added fw-ohci device %s, OHCI version %x.%x\n", - dev->dev.bus_id, ohci->version >> 16, ohci->version & 0xff); + dev->dev.bus_id, version >> 16, version & 0xff); return 0; fail_self_id: diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 65953822c9cb..720d67554106 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -748,6 +748,7 @@ #define PCI_VENDOR_ID_TI 0x104c #define PCI_DEVICE_ID_TI_TVP4020 0x3d07 #define PCI_DEVICE_ID_TI_4450 0x8011 +#define PCI_DEVICE_ID_TI_TSB43AB22 0x8023 #define PCI_DEVICE_ID_TI_XX21_XX11 0x8031 #define PCI_DEVICE_ID_TI_XX21_XX11_FM 0x8033 #define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034 -- cgit From 3d45955962496879dead8d4dd70bb9a23b07154b Mon Sep 17 00:00:00 2001 From: Alexey Korolev Date: Thu, 15 May 2008 17:23:18 +0100 Subject: [MTD] [NAND] subpage read feature as a way to increase performance. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch enables NAND subpage read functionality. If upper layer drivers are requesting to read non page aligned data NAND subpage-read functionality reads the only whose ECC regions which include requested data when original code reads whole page. This significantly improves performance in many cases. Here are some digits : UBI volume mount time No subpage reads: 5.75 seconds Subpage read patch: 2.42 seconds Open/stat time for files on JFFS2 volume: No subpage read 0m 5.36s Subpage read 0m 2.88s Signed-off-by Alexey Korolev Acked-by: Artem Bityutskiy Acked-by: Jörn Engel Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 87 +++++++++++++++++++++++++++++++++++++++++++- include/linux/mtd/nand.h | 5 +++ 2 files changed, 91 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ba1bdf787323..d1129bae6c27 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -797,6 +797,87 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +/** + * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @dataofs offset of requested data within the page + * @readlen data length + * @buf: buffer to store read data + */ +static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) +{ + int start_step, end_step, num_steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p; + int data_col_addr, i, gaps = 0; + int datafrag_len, eccfrag_len, aligned_len, aligned_pos; + int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + + /* Column address wihin the page aligned to ECC size (256bytes). */ + start_step = data_offs / chip->ecc.size; + end_step = (data_offs + readlen - 1) / chip->ecc.size; + num_steps = end_step - start_step + 1; + + /* Data size aligned to ECC ecc.size*/ + datafrag_len = num_steps * chip->ecc.size; + eccfrag_len = num_steps * chip->ecc.bytes; + + data_col_addr = start_step * chip->ecc.size; + /* If we read not a page aligned data */ + if (data_col_addr != 0) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); + + p = bufpoi + data_col_addr; + chip->read_buf(mtd, p, datafrag_len); + + /* Calculate ECC */ + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) + chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + + /* The performance is faster if to position offsets + according to ecc.pos. Let make sure here that + there are no gaps in ecc positions */ + for (i = 0; i < eccfrag_len - 1; i++) { + if (eccpos[i + start_step * chip->ecc.bytes] + 1 != + eccpos[i + start_step * chip->ecc.bytes + 1]) { + gaps = 1; + break; + } + } + if (gaps) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + } else { + /* send the command to read the particular ecc bytes */ + /* take care about buswidth alignment in read_buf */ + aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1); + aligned_len = eccfrag_len; + if (eccpos[start_step * chip->ecc.bytes] & (busw - 1)) + aligned_len++; + if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1)) + aligned_len++; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); + chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + } + + for (i = 0; i < eccfrag_len; i++) + chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]]; + + p = bufpoi + data_col_addr; + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { + int stat; + + stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + if (stat == -1) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + /** * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure @@ -994,6 +1075,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); + else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) + ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi); if (ret < 0) @@ -1001,7 +1084,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Transfer not aligned data */ if (!aligned) { - chip->pagebuf = realpage; + if (!NAND_SUBPAGE_READ(chip) && !oob) + chip->pagebuf = realpage; memcpy(buf, chip->buffers->databuf + col, bytes); } @@ -2521,6 +2605,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.read_subpage = nand_read_subpage; chip->ecc.write_page = nand_write_page_swecc; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 863e22a0ddb5..83f678702dff 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -177,6 +177,7 @@ typedef enum { #define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) #define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) +#define NAND_SUBPAGE_READ(chip) ((chip->ecc.mode == NAND_ECC_SOFT)) /* Mask to zero out the chip options, which come from the id table */ #define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) @@ -274,6 +275,10 @@ struct nand_ecc_ctrl { int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf); + int (*read_subpage)(struct mtd_info *mtd, + struct nand_chip *chip, + uint32_t offs, uint32_t len, + uint8_t *buf); void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf); -- cgit From 3d6f4a20cc287a8980c6186624834cf10a70752b Mon Sep 17 00:00:00 2001 From: David Miller Date: Thu, 24 Jul 2008 23:38:31 -0700 Subject: endian: Always evaluate arguments. Changeset 7fa897b91a3ea0f16c2873b869d7a0eef05acff4 ("ide: trivial sparse annotations") created an IDE bootup regression on big-endian systems. In drivers/ide/ide-iops.c, function ide_fixstring() we now have the loop: for (p = end ; p != s;) be16_to_cpus((u16 *)(p -= 2)); which will never terminate on big-endian because in such a configuration be16_to_cpus() evaluates to "do { } while (0)" Therefore, always evaluate the arguments to nop endian transformation operations. Signed-off-by: David S. Miller Signed-off-by: Linus Torvalds --- include/linux/byteorder/big_endian.h | 12 ++++++------ include/linux/byteorder/little_endian.h | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/byteorder/big_endian.h b/include/linux/byteorder/big_endian.h index 961ed4b48d8e..44f95b92393b 100644 --- a/include/linux/byteorder/big_endian.h +++ b/include/linux/byteorder/big_endian.h @@ -94,12 +94,12 @@ static inline __u16 __be16_to_cpup(const __be16 *p) #define __le32_to_cpus(x) __swab32s((x)) #define __cpu_to_le16s(x) __swab16s((x)) #define __le16_to_cpus(x) __swab16s((x)) -#define __cpu_to_be64s(x) do {} while (0) -#define __be64_to_cpus(x) do {} while (0) -#define __cpu_to_be32s(x) do {} while (0) -#define __be32_to_cpus(x) do {} while (0) -#define __cpu_to_be16s(x) do {} while (0) -#define __be16_to_cpus(x) do {} while (0) +#define __cpu_to_be64s(x) do { (void)(x); } while (0) +#define __be64_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_be32s(x) do { (void)(x); } while (0) +#define __be32_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_be16s(x) do { (void)(x); } while (0) +#define __be16_to_cpus(x) do { (void)(x); } while (0) #ifdef __KERNEL__ #include diff --git a/include/linux/byteorder/little_endian.h b/include/linux/byteorder/little_endian.h index 05dc7c35b3b2..4cc170a31762 100644 --- a/include/linux/byteorder/little_endian.h +++ b/include/linux/byteorder/little_endian.h @@ -88,12 +88,12 @@ static inline __u16 __be16_to_cpup(const __be16 *p) { return __swab16p((__u16 *)p); } -#define __cpu_to_le64s(x) do {} while (0) -#define __le64_to_cpus(x) do {} while (0) -#define __cpu_to_le32s(x) do {} while (0) -#define __le32_to_cpus(x) do {} while (0) -#define __cpu_to_le16s(x) do {} while (0) -#define __le16_to_cpus(x) do {} while (0) +#define __cpu_to_le64s(x) do { (void)(x); } while (0) +#define __le64_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_le32s(x) do { (void)(x); } while (0) +#define __le32_to_cpus(x) do { (void)(x); } while (0) +#define __cpu_to_le16s(x) do { (void)(x); } while (0) +#define __le16_to_cpus(x) do { (void)(x); } while (0) #define __cpu_to_be64s(x) __swab64s((x)) #define __be64_to_cpus(x) __swab64s((x)) #define __cpu_to_be32s(x) __swab32s((x)) -- cgit From 25c94d010a8ae8605dc4d5453e0c82fa97da5d12 Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Fri, 25 Jul 2008 10:30:06 -0700 Subject: mlx4_core: Add VLAN tag field to WQE control segment struct Add fields for VLAN tag and insert VLAN tag flag to the control section struct. These fields will be used for sending ethernet packets. Signed-off-by: Yevgeny Petrilin Signed-off-by: Roland Dreier --- include/linux/mlx4/qp.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index e27082cd650e..bf8f11982dae 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -164,11 +164,13 @@ enum { MLX4_WQE_CTRL_SOLICITED = 1 << 1, MLX4_WQE_CTRL_IP_CSUM = 1 << 4, MLX4_WQE_CTRL_TCP_UDP_CSUM = 1 << 5, + MLX4_WQE_CTRL_INS_VLAN = 1 << 6, }; struct mlx4_wqe_ctrl_seg { __be32 owner_opcode; - u8 reserved2[3]; + __be16 vlan_tag; + u8 ins_vlan; u8 fence_size; /* * High 24 bits are SRC remote buffer; low 8 bits are flags: -- cgit From e0deaff470900a4c3222ca7139f6c9639e26a2f5 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 25 Jul 2008 01:45:24 -0700 Subject: split the typecheck macros out of include/linux/kernel.h Needed to fix up a recursive include snafu in locking-add-typecheck-on-irqsave-and-friends-for-correct-flags.patch Cc: Steven Rostedt Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kernel.h | 21 +-------------------- include/linux/typecheck.h | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 include/linux/typecheck.h (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index f9cd7a513f9c..5c4b1251e110 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -441,26 +442,6 @@ static inline char *pack_hex_byte(char *buf, u8 byte) const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) -/* - * Check at compile time that something is of a particular type. - * Always evaluates to 1 so you may use it easily in comparisons. - */ -#define typecheck(type,x) \ -({ type __dummy; \ - typeof(x) __dummy2; \ - (void)(&__dummy == &__dummy2); \ - 1; \ -}) - -/* - * Check at compile time that 'function' is a certain type, or is a pointer - * to that type (needs to use typedef for the function type.) - */ -#define typecheck_fn(type,function) \ -({ typeof(type) __tmp = function; \ - (void)__tmp; \ -}) - struct sysinfo; extern int do_sysinfo(struct sysinfo *info); diff --git a/include/linux/typecheck.h b/include/linux/typecheck.h new file mode 100644 index 000000000000..eb5b74a575be --- /dev/null +++ b/include/linux/typecheck.h @@ -0,0 +1,24 @@ +#ifndef TYPECHECK_H_INCLUDED +#define TYPECHECK_H_INCLUDED + +/* + * Check at compile time that something is of a particular type. + * Always evaluates to 1 so you may use it easily in comparisons. + */ +#define typecheck(type,x) \ +({ type __dummy; \ + typeof(x) __dummy2; \ + (void)(&__dummy == &__dummy2); \ + 1; \ +}) + +/* + * Check at compile time that 'function' is a certain type, or is a pointer + * to that type (needs to use typedef for the function type.) + */ +#define typecheck_fn(type,function) \ +({ typeof(type) __tmp = function; \ + (void)__tmp; \ +}) + +#endif /* TYPECHECK_H_INCLUDED */ -- cgit From 3f307891ce0e7b0438c432af1aacd656a092ff45 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 25 Jul 2008 01:45:25 -0700 Subject: locking: add typecheck on irqsave and friends for correct flags There haave been several areas in the kernel where an int has been used for flags in local_irq_save() and friends instead of a long. This can cause some hard to debug problems on some architectures. This patch adds a typecheck inside the irqsave and restore functions to flag these cases. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: build fix] Signed-off-by: Steven Rostedt Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/irqflags.h | 54 ++++++++++++++++++++++++++---------- include/linux/spinlock.h | 72 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 95 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h index 2b1c2e58566e..74bde13224c9 100644 --- a/include/linux/irqflags.h +++ b/include/linux/irqflags.h @@ -11,6 +11,8 @@ #ifndef _LINUX_TRACE_IRQFLAGS_H #define _LINUX_TRACE_IRQFLAGS_H +#include + #ifdef CONFIG_TRACE_IRQFLAGS extern void trace_softirqs_on(unsigned long ip); extern void trace_softirqs_off(unsigned long ip); @@ -58,18 +60,24 @@ do { trace_hardirqs_on(); raw_local_irq_enable(); } while (0) #define local_irq_disable() \ do { raw_local_irq_disable(); trace_hardirqs_off(); } while (0) -#define local_irq_save(flags) \ - do { raw_local_irq_save(flags); trace_hardirqs_off(); } while (0) +#define local_irq_save(flags) \ + do { \ + typecheck(unsigned long, flags); \ + raw_local_irq_save(flags); \ + trace_hardirqs_off(); \ + } while (0) -#define local_irq_restore(flags) \ - do { \ - if (raw_irqs_disabled_flags(flags)) { \ - raw_local_irq_restore(flags); \ - trace_hardirqs_off(); \ - } else { \ - trace_hardirqs_on(); \ - raw_local_irq_restore(flags); \ - } \ + +#define local_irq_restore(flags) \ + do { \ + typecheck(unsigned long, flags); \ + if (raw_irqs_disabled_flags(flags)) { \ + raw_local_irq_restore(flags); \ + trace_hardirqs_off(); \ + } else { \ + trace_hardirqs_on(); \ + raw_local_irq_restore(flags); \ + } \ } while (0) #else /* !CONFIG_TRACE_IRQFLAGS_SUPPORT */ /* @@ -78,8 +86,16 @@ */ # define raw_local_irq_disable() local_irq_disable() # define raw_local_irq_enable() local_irq_enable() -# define raw_local_irq_save(flags) local_irq_save(flags) -# define raw_local_irq_restore(flags) local_irq_restore(flags) +# define raw_local_irq_save(flags) \ + do { \ + typecheck(unsigned long, flags); \ + local_irq_save(flags); \ + } while (0) +# define raw_local_irq_restore(flags) \ + do { \ + typecheck(unsigned long, flags); \ + local_irq_restore(flags); \ + } while (0) #endif /* CONFIG_TRACE_IRQFLAGS_SUPPORT */ #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT @@ -89,7 +105,11 @@ raw_safe_halt(); \ } while (0) -#define local_save_flags(flags) raw_local_save_flags(flags) +#define local_save_flags(flags) \ + do { \ + typecheck(unsigned long, flags); \ + raw_local_save_flags(flags); \ + } while (0) #define irqs_disabled() \ ({ \ @@ -99,7 +119,11 @@ raw_irqs_disabled_flags(_flags); \ }) -#define irqs_disabled_flags(flags) raw_irqs_disabled_flags(flags) +#define irqs_disabled_flags(flags) \ +({ \ + typecheck(unsigned long, flags); \ + raw_irqs_disabled_flags(flags); \ +}) #endif /* CONFIG_X86 */ #endif diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index d311a090fae7..61e5610ad165 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -46,6 +46,7 @@ * linux/spinlock.h: builds the final spin_*() APIs. */ +#include #include #include #include @@ -191,23 +192,53 @@ do { \ #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) -#define spin_lock_irqsave(lock, flags) flags = _spin_lock_irqsave(lock) -#define read_lock_irqsave(lock, flags) flags = _read_lock_irqsave(lock) -#define write_lock_irqsave(lock, flags) flags = _write_lock_irqsave(lock) +#define spin_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _spin_lock_irqsave(lock); \ + } while (0) +#define read_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _read_lock_irqsave(lock); \ + } while (0) +#define write_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _write_lock_irqsave(lock); \ + } while (0) #ifdef CONFIG_DEBUG_LOCK_ALLOC -#define spin_lock_irqsave_nested(lock, flags, subclass) \ - flags = _spin_lock_irqsave_nested(lock, subclass) +#define spin_lock_irqsave_nested(lock, flags, subclass) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _spin_lock_irqsave_nested(lock, subclass); \ + } while (0) #else -#define spin_lock_irqsave_nested(lock, flags, subclass) \ - flags = _spin_lock_irqsave(lock) +#define spin_lock_irqsave_nested(lock, flags, subclass) \ + do { \ + typecheck(unsigned long, flags); \ + flags = _spin_lock_irqsave(lock); \ + } while (0) #endif #else -#define spin_lock_irqsave(lock, flags) _spin_lock_irqsave(lock, flags) -#define read_lock_irqsave(lock, flags) _read_lock_irqsave(lock, flags) -#define write_lock_irqsave(lock, flags) _write_lock_irqsave(lock, flags) +#define spin_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _spin_lock_irqsave(lock, flags); \ + } while (0) +#define read_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _read_lock_irqsave(lock, flags); \ + } while (0) +#define write_lock_irqsave(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _write_lock_irqsave(lock, flags); \ + } while (0) #define spin_lock_irqsave_nested(lock, flags, subclass) \ spin_lock_irqsave(lock, flags) @@ -260,16 +291,25 @@ do { \ } while (0) #endif -#define spin_unlock_irqrestore(lock, flags) \ - _spin_unlock_irqrestore(lock, flags) +#define spin_unlock_irqrestore(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _spin_unlock_irqrestore(lock, flags); \ + } while (0) #define spin_unlock_bh(lock) _spin_unlock_bh(lock) -#define read_unlock_irqrestore(lock, flags) \ - _read_unlock_irqrestore(lock, flags) +#define read_unlock_irqrestore(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _read_unlock_irqrestore(lock, flags); \ + } while (0) #define read_unlock_bh(lock) _read_unlock_bh(lock) -#define write_unlock_irqrestore(lock, flags) \ - _write_unlock_irqrestore(lock, flags) +#define write_unlock_irqrestore(lock, flags) \ + do { \ + typecheck(unsigned long, flags); \ + _write_unlock_irqrestore(lock, flags); \ + } while (0) #define write_unlock_bh(lock) _write_unlock_bh(lock) #define spin_trylock_bh(lock) __cond_lock(lock, _spin_trylock_bh(lock)) -- cgit From 8b5ac31e27135a6f2c210c40d03bf8f1b3a86b77 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Fri, 25 Jul 2008 01:45:26 -0700 Subject: include: use get/put_unaligned_* helpers Signed-off-by: Harvey Harrison Cc: "John W. Linville" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/reiserfs_fs.h | 4 ++-- include/linux/smb_fs.h | 19 +++++++------------ include/net/ieee80211_radiotap.h | 2 +- 3 files changed, 10 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h index 4aacaeecb56f..e9963af16cda 100644 --- a/include/linux/reiserfs_fs.h +++ b/include/linux/reiserfs_fs.h @@ -526,8 +526,8 @@ struct item_head { ** p is the array of __u32, i is the index into the array, v is the value ** to store there. */ -#define get_block_num(p, i) le32_to_cpu(get_unaligned((p) + (i))) -#define put_block_num(p, i, v) put_unaligned(cpu_to_le32(v), (p) + (i)) +#define get_block_num(p, i) get_unaligned_le32((p) + (i)) +#define put_block_num(p, i, v) put_unaligned_le32((v), (p) + (i)) // // in old version uniqueness field shows key type diff --git a/include/linux/smb_fs.h b/include/linux/smb_fs.h index 2c5cd55f44ff..923cd8a247b1 100644 --- a/include/linux/smb_fs.h +++ b/include/linux/smb_fs.h @@ -43,18 +43,13 @@ static inline struct smb_inode_info *SMB_I(struct inode *inode) } /* macro names are short for word, double-word, long value (?) */ -#define WVAL(buf,pos) \ - (le16_to_cpu(get_unaligned((__le16 *)((u8 *)(buf) + (pos))))) -#define DVAL(buf,pos) \ - (le32_to_cpu(get_unaligned((__le32 *)((u8 *)(buf) + (pos))))) -#define LVAL(buf,pos) \ - (le64_to_cpu(get_unaligned((__le64 *)((u8 *)(buf) + (pos))))) -#define WSET(buf,pos,val) \ - put_unaligned(cpu_to_le16((u16)(val)), (__le16 *)((u8 *)(buf) + (pos))) -#define DSET(buf,pos,val) \ - put_unaligned(cpu_to_le32((u32)(val)), (__le32 *)((u8 *)(buf) + (pos))) -#define LSET(buf,pos,val) \ - put_unaligned(cpu_to_le64((u64)(val)), (__le64 *)((u8 *)(buf) + (pos))) +#define WVAL(buf, pos) (get_unaligned_le16((u8 *)(buf) + (pos))) +#define DVAL(buf, pos) (get_unaligned_le32((u8 *)(buf) + (pos))) +#define LVAL(buf, pos) (get_unaligned_le64((u8 *)(buf) + (pos))) + +#define WSET(buf, pos, val) put_unaligned_le16((val), (u8 *)(buf) + (pos)) +#define DSET(buf, pos, val) put_unaligned_le32((val), (u8 *)(buf) + (pos)) +#define LSET(buf, pos, val) put_unaligned_le64((val), (u8 *)(buf) + (pos)) /* where to find the base of the SMB packet proper */ #define smb_base(buf) ((u8 *)(((u8 *)(buf))+4)) diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index dfd8bf66ce27..d364fd594ea4 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -262,7 +262,7 @@ static inline int ieee80211_get_radiotap_len(unsigned char *data) struct ieee80211_radiotap_header *hdr = (struct ieee80211_radiotap_header *)data; - return le16_to_cpu(get_unaligned(&hdr->it_len)); + return get_unaligned_le16(&hdr->it_len); } #endif /* IEEE80211_RADIOTAP_H */ -- cgit From b39c08cb692cb8898c30e0d8187c7cbe27cc905c Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Fri, 25 Jul 2008 01:45:29 -0700 Subject: Remove apparently unused fd1772.h header file. This header file has been unused for quite some time, and the corresponding source files appear to have been removed back in commit 99eb8a550dbccc0e1f6c7e866fe421810e0585f6 ("Remove the arm26 port") Signed-off-by: Robert P. J. Day Cc: Adrian Bunk Cc: Ian Molton Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/fd1772.h | 80 -------------------------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 include/linux/fd1772.h (limited to 'include/linux') diff --git a/include/linux/fd1772.h b/include/linux/fd1772.h deleted file mode 100644 index 871d6e4c677e..000000000000 --- a/include/linux/fd1772.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef _LINUX_FD1772REG_H -#define _LINUX_FD1772REG_H - -/* -** WD1772 stuff - originally from the M68K Linux - * Modified for Archimedes by Dave Gilbert (gilbertd@cs.man.ac.uk) - */ - -/* register codes */ - -#define FDC1772SELREG_STP (0x80) /* command/status register */ -#define FDC1772SELREG_TRA (0x82) /* track register */ -#define FDC1772SELREG_SEC (0x84) /* sector register */ -#define FDC1772SELREG_DTA (0x86) /* data register */ - -/* register names for FDC1772_READ/WRITE macros */ - -#define FDC1772REG_CMD 0 -#define FDC1772REG_STATUS 0 -#define FDC1772REG_TRACK 2 -#define FDC1772REG_SECTOR 4 -#define FDC1772REG_DATA 6 - -/* command opcodes */ - -#define FDC1772CMD_RESTORE (0x00) /* - */ -#define FDC1772CMD_SEEK (0x10) /* | */ -#define FDC1772CMD_STEP (0x20) /* | TYP 1 Commands */ -#define FDC1772CMD_STIN (0x40) /* | */ -#define FDC1772CMD_STOT (0x60) /* - */ -#define FDC1772CMD_RDSEC (0x80) /* - TYP 2 Commands */ -#define FDC1772CMD_WRSEC (0xa0) /* - " */ -#define FDC1772CMD_RDADR (0xc0) /* - */ -#define FDC1772CMD_RDTRA (0xe0) /* | TYP 3 Commands */ -#define FDC1772CMD_WRTRA (0xf0) /* - */ -#define FDC1772CMD_FORCI (0xd0) /* - TYP 4 Command */ - -/* command modifier bits */ - -#define FDC1772CMDADD_SR6 (0x00) /* step rate settings */ -#define FDC1772CMDADD_SR12 (0x01) -#define FDC1772CMDADD_SR2 (0x02) -#define FDC1772CMDADD_SR3 (0x03) -#define FDC1772CMDADD_V (0x04) /* verify */ -#define FDC1772CMDADD_H (0x08) /* wait for spin-up */ -#define FDC1772CMDADD_U (0x10) /* update track register */ -#define FDC1772CMDADD_M (0x10) /* multiple sector access */ -#define FDC1772CMDADD_E (0x04) /* head settling flag */ -#define FDC1772CMDADD_P (0x02) /* precompensation */ -#define FDC1772CMDADD_A0 (0x01) /* DAM flag */ - -/* status register bits */ - -#define FDC1772STAT_MOTORON (0x80) /* motor on */ -#define FDC1772STAT_WPROT (0x40) /* write protected (FDC1772CMD_WR*) */ -#define FDC1772STAT_SPINUP (0x20) /* motor speed stable (Type I) */ -#define FDC1772STAT_DELDAM (0x20) /* sector has deleted DAM (Type II+III) */ -#define FDC1772STAT_RECNF (0x10) /* record not found */ -#define FDC1772STAT_CRC (0x08) /* CRC error */ -#define FDC1772STAT_TR00 (0x04) /* Track 00 flag (Type I) */ -#define FDC1772STAT_LOST (0x04) /* Lost Data (Type II+III) */ -#define FDC1772STAT_IDX (0x02) /* Index status (Type I) */ -#define FDC1772STAT_DRQ (0x02) /* DRQ status (Type II+III) */ -#define FDC1772STAT_BUSY (0x01) /* FDC1772 is busy */ - - -/* PSG Port A Bit Nr 0 .. Side Sel .. 0 -> Side 1 1 -> Side 2 */ -#define DSKSIDE (0x01) - -#define DSKDRVNONE (0x06) -#define DSKDRV0 (0x02) -#define DSKDRV1 (0x04) - -/* step rates */ -#define FDC1772STEP_6 0x00 -#define FDC1772STEP_12 0x01 -#define FDC1772STEP_2 0x02 -#define FDC1772STEP_3 0x03 - -#endif -- cgit From e0ce0da9fefcc723dc006c35a7f91a32750abd40 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Fri, 25 Jul 2008 01:45:32 -0700 Subject: lists: remove a redundant conditional definition of list_add() Remove the conditional surrounding the definition of list_add() from list.h since, if you define CONFIG_DEBUG_LIST, the definition you will subsequently pick up from lib/list_debug.c will be absolutely identical, at which point you can remove that redundant definition from list_debug.c as well. Signed-off-by: Robert P. J. Day Cc: Dave Jones Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/list.h | 4 ---- lib/list_debug.c | 14 -------------- 2 files changed, 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/list.h b/include/linux/list.h index 139ec41d9c2e..453916bc0412 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -61,14 +61,10 @@ extern void __list_add(struct list_head *new, * Insert a new entry after the specified head. * This is good for implementing stacks. */ -#ifndef CONFIG_DEBUG_LIST static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } -#else -extern void list_add(struct list_head *new, struct list_head *head); -#endif /** diff --git a/lib/list_debug.c b/lib/list_debug.c index 4350ba9655bd..45c03fd608dd 100644 --- a/lib/list_debug.c +++ b/lib/list_debug.c @@ -39,20 +39,6 @@ void __list_add(struct list_head *new, } EXPORT_SYMBOL(__list_add); -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} -EXPORT_SYMBOL(list_add); - /** * list_del - deletes entry from list. * @entry: the element to delete from the list. -- cgit From b03f6489f9f27dc519a4c60ebf39cc7b8a58eae7 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:45:35 -0700 Subject: build kernel/profile.o only when requested Build kernel/profile.o only if CONFIG_PROFILING is enabled. This makes CONFIG_PROFILING=n kernels smaller. As a bonus, some profile_tick() calls and one branch from schedule() are now eliminated with CONFIG_PROFILING=n (but I doubt these are measurable effects). This patch changes the effects of CONFIG_PROFILING=n, but I don't think having more than two choices would be the better choice. This patch also adds the name of the first parameter to the prototypes of profile_{hits,tick}() since I anyway had to add them for the dummy functions. Signed-off-by: Adrian Bunk Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/profile.h | 56 ++++++++++++++++++++++++++++++++++--------------- kernel/Makefile | 3 ++- kernel/profile.c | 4 ---- 3 files changed, 41 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/profile.h b/include/linux/profile.h index 05c1cc736937..4081fa31081f 100644 --- a/include/linux/profile.h +++ b/include/linux/profile.h @@ -8,8 +8,6 @@ #include -extern int prof_on __read_mostly; - #define CPU_PROFILING 1 #define SCHED_PROFILING 2 #define SLEEP_PROFILING 3 @@ -19,14 +17,29 @@ struct proc_dir_entry; struct pt_regs; struct notifier_block; +#if defined(CONFIG_PROFILING) && defined(CONFIG_PROC_FS) +void create_prof_cpu_mask(struct proc_dir_entry *); +#else +#define create_prof_cpu_mask(x) do { (void)(x); } while (0) +#endif + +enum profile_type { + PROFILE_TASK_EXIT, + PROFILE_MUNMAP +}; + +#ifdef CONFIG_PROFILING + +extern int prof_on __read_mostly; + /* init basic kernel profiler */ void __init profile_init(void); -void profile_tick(int); +void profile_tick(int type); /* * Add multiple profiler hits to a given address: */ -void profile_hits(int, void *ip, unsigned int nr_hits); +void profile_hits(int type, void *ip, unsigned int nr_hits); /* * Single profiler hit: @@ -40,19 +53,6 @@ static inline void profile_hit(int type, void *ip) profile_hits(type, ip, 1); } -#ifdef CONFIG_PROC_FS -void create_prof_cpu_mask(struct proc_dir_entry *); -#else -#define create_prof_cpu_mask(x) do { (void)(x); } while (0) -#endif - -enum profile_type { - PROFILE_TASK_EXIT, - PROFILE_MUNMAP -}; - -#ifdef CONFIG_PROFILING - struct task_struct; struct mm_struct; @@ -80,6 +80,28 @@ struct pt_regs; #else +#define prof_on 0 + +static inline void profile_init(void) +{ + return; +} + +static inline void profile_tick(int type) +{ + return; +} + +static inline void profile_hits(int type, void *ip, unsigned int nr_hits) +{ + return; +} + +static inline void profile_hit(int type, void *ip) +{ + return; +} + static inline int task_handoff_register(struct notifier_block * n) { return -ENOSYS; diff --git a/kernel/Makefile b/kernel/Makefile index 15ab63ffe64d..54f69837d35a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux kernel. # -obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ +obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ cpu.o exit.o itimer.o time.o softirq.o resource.o \ sysctl.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o workqueue.o pid.o \ @@ -24,6 +24,7 @@ CFLAGS_REMOVE_sched_clock.o = -pg CFLAGS_REMOVE_sched.o = -mno-spe -pg endif +obj-$(CONFIG_PROFILING) += profile.o obj-$(CONFIG_SYSCTL_SYSCALL_CHECK) += sysctl_check.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/ diff --git a/kernel/profile.c b/kernel/profile.c index 58926411eb2a..cd26bed4cc26 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -112,8 +112,6 @@ void __init profile_init(void) /* Profile event notifications */ -#ifdef CONFIG_PROFILING - static BLOCKING_NOTIFIER_HEAD(task_exit_notifier); static ATOMIC_NOTIFIER_HEAD(task_free_notifier); static BLOCKING_NOTIFIER_HEAD(munmap_notifier); @@ -203,8 +201,6 @@ void unregister_timer_hook(int (*hook)(struct pt_regs *)) } EXPORT_SYMBOL_GPL(unregister_timer_hook); -#endif /* CONFIG_PROFILING */ - #ifdef CONFIG_SMP /* -- cgit From cebbd3fb803603b12408458ba17c29ce1e15a5f2 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 25 Jul 2008 01:45:35 -0700 Subject: build-kernel-profileo-only-when-requested-cleanups Cc: Adrian Bunk Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/profile.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/profile.h b/include/linux/profile.h index 4081fa31081f..7e7087239af5 100644 --- a/include/linux/profile.h +++ b/include/linux/profile.h @@ -18,9 +18,11 @@ struct pt_regs; struct notifier_block; #if defined(CONFIG_PROFILING) && defined(CONFIG_PROC_FS) -void create_prof_cpu_mask(struct proc_dir_entry *); +void create_prof_cpu_mask(struct proc_dir_entry *de); #else -#define create_prof_cpu_mask(x) do { (void)(x); } while (0) +static inline void create_prof_cpu_mask(struct proc_dir_entry *de) +{ +} #endif enum profile_type { -- cgit From ac331d158e198d2a91a5b0a3ec4ca9991fdb57af Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Fri, 25 Jul 2008 01:45:38 -0700 Subject: call_usermodehelper(): increase reliability Presently call_usermodehelper_setup() uses GFP_ATOMIC. but it can return NULL _very_ easily. GFP_ATOMIC is needed only when we can't sleep. and, GFP_KERNEL is robust and better. thus, I add gfp_mask argument to call_usermodehelper_setup(). So, its callers pass the gfp_t as below: call_usermodehelper() and call_usermodehelper_keys(): depend on 'wait' argument. call_usermodehelper_pipe(): always GFP_KERNEL because always run under process context. orderly_poweroff(): pass to GFP_ATOMIC because may run under interrupt context. Signed-off-by: KOSAKI Motohiro Cc: "Paul Menage" Reviewed-by: Li Zefan Acked-by: Jeremy Fitzhardinge Cc: Rusty Russell Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kmod.h | 11 +++++++---- kernel/kmod.c | 9 +++++---- kernel/sys.c | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 0509c4ce4857..a1a91577813c 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -19,6 +19,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include @@ -41,8 +42,8 @@ struct file; struct subprocess_info; /* Allocate a subprocess_info structure */ -struct subprocess_info *call_usermodehelper_setup(char *path, - char **argv, char **envp); +struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, + char **envp, gfp_t gfp_mask); /* Set various pieces of state into the subprocess_info structure */ void call_usermodehelper_setkeys(struct subprocess_info *info, @@ -69,8 +70,9 @@ static inline int call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait) { struct subprocess_info *info; + gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; - info = call_usermodehelper_setup(path, argv, envp); + info = call_usermodehelper_setup(path, argv, envp, gfp_mask); if (info == NULL) return -ENOMEM; return call_usermodehelper_exec(info, wait); @@ -81,8 +83,9 @@ call_usermodehelper_keys(char *path, char **argv, char **envp, struct key *session_keyring, enum umh_wait wait) { struct subprocess_info *info; + gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; - info = call_usermodehelper_setup(path, argv, envp); + info = call_usermodehelper_setup(path, argv, envp, gfp_mask); if (info == NULL) return -ENOMEM; diff --git a/kernel/kmod.c b/kernel/kmod.c index 2989f67c4446..2456d1a0befb 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -352,16 +352,17 @@ static inline void register_pm_notifier_callback(void) {} * @path: path to usermode executable * @argv: arg vector for process * @envp: environment for process + * @gfp_mask: gfp mask for memory allocation * * Returns either %NULL on allocation failure, or a subprocess_info * structure. This should be passed to call_usermodehelper_exec to * exec the process and free the structure. */ -struct subprocess_info *call_usermodehelper_setup(char *path, - char **argv, char **envp) +struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, + char **envp, gfp_t gfp_mask) { struct subprocess_info *sub_info; - sub_info = kzalloc(sizeof(struct subprocess_info), GFP_ATOMIC); + sub_info = kzalloc(sizeof(struct subprocess_info), gfp_mask); if (!sub_info) goto out; @@ -494,7 +495,7 @@ int call_usermodehelper_pipe(char *path, char **argv, char **envp, struct subprocess_info *sub_info; int ret; - sub_info = call_usermodehelper_setup(path, argv, envp); + sub_info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL); if (sub_info == NULL) return -ENOMEM; diff --git a/kernel/sys.c b/kernel/sys.c index 14e97282eb6c..6c2188046048 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1795,7 +1795,7 @@ int orderly_poweroff(bool force) goto out; } - info = call_usermodehelper_setup(argv[0], argv, envp); + info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC); if (info == NULL) { argv_free(argv); goto out; -- cgit From 4500d067eeb3d00679335d9cf5c6536e79cd3ef4 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Fri, 25 Jul 2008 01:45:49 -0700 Subject: init.h: remove obsolete content Remove apparently obsolete content from init.h referring to gcc 2.9x and to "no_module_init". Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/init.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/init.h b/include/linux/init.h index 21d658cdfa27..42ae95411a93 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -275,13 +275,7 @@ void __init parse_early_param(void); #define security_initcall(fn) module_init(fn) -/* These macros create a dummy inline: gcc 2.9x does not count alias - as usage, hence the `unused function' warning when __init functions - are declared static. We use the dummy __*_module_inline functions - both to kill the warning and check the type of the init/cleanup - function. */ - -/* Each module must use one module_init(), or one no_module_init */ +/* Each module must use one module_init(). */ #define module_init(initfn) \ static inline initcall_t __inittest(void) \ { return initfn; } \ -- cgit From b6c63937001889af6fe431aaba97e59d04e028e7 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Fri, 25 Jul 2008 01:45:52 -0700 Subject: Rename WARN() to WARNING() to clear the namespace We want to use WARN() as a variant of WARN_ON(), however a few drivers are using WARN() internally. This patch renames these to WARNING() to avoid the namespace clash. A few cases were defining but not using the thing, for those cases I just deleted the definition. Signed-off-by: Arjan van de Ven Acked-by: Greg KH Cc: Karsten Keil Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/isdn/hisax/st5481.h | 4 ++-- drivers/isdn/hisax/st5481_b.c | 4 ++-- drivers/isdn/hisax/st5481_d.c | 6 +++--- drivers/isdn/hisax/st5481_usb.c | 18 +++++++++--------- drivers/usb/gadget/at91_udc.h | 2 +- drivers/usb/gadget/cdc2.c | 2 +- drivers/usb/gadget/ether.c | 2 +- drivers/usb/gadget/file_storage.c | 14 +++++++------- drivers/usb/gadget/fsl_usb2_udc.c | 2 +- drivers/usb/gadget/fsl_usb2_udc.h | 2 +- drivers/usb/gadget/gmidi.c | 2 -- drivers/usb/gadget/goku_udc.c | 2 +- drivers/usb/gadget/goku_udc.h | 2 +- drivers/usb/gadget/inode.c | 2 -- drivers/usb/gadget/net2280.c | 2 +- drivers/usb/gadget/net2280.h | 2 +- drivers/usb/gadget/omap_udc.c | 6 +++--- drivers/usb/gadget/omap_udc.h | 2 +- drivers/usb/gadget/printer.c | 2 +- drivers/usb/gadget/pxa25x_udc.c | 6 +++--- drivers/usb/gadget/pxa25x_udc.h | 2 +- drivers/usb/gadget/u_ether.c | 3 --- drivers/usb/host/isp116x-hcd.c | 2 +- drivers/usb/host/isp116x.h | 2 +- drivers/usb/host/sl811-hcd.c | 2 +- drivers/usb/host/sl811.h | 2 +- drivers/usb/misc/usbtest.c | 4 ++-- include/linux/usb/composite.h | 2 +- 28 files changed, 48 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/hisax/st5481.h b/drivers/isdn/hisax/st5481.h index 2044e7173ab4..cff7a6354334 100644 --- a/drivers/isdn/hisax/st5481.h +++ b/drivers/isdn/hisax/st5481.h @@ -220,7 +220,7 @@ enum { #define ERR(format, arg...) \ printk(KERN_ERR "%s:%s: " format "\n" , __FILE__, __func__ , ## arg) -#define WARN(format, arg...) \ +#define WARNING(format, arg...) \ printk(KERN_WARNING "%s:%s: " format "\n" , __FILE__, __func__ , ## arg) #define INFO(format, arg...) \ @@ -412,7 +412,7 @@ struct st5481_adapter { ({ \ int status; \ if ((status = usb_submit_urb(urb, mem_flags)) < 0) { \ - WARN("usb_submit_urb failed,status=%d", status); \ + WARNING("usb_submit_urb failed,status=%d", status); \ } \ status; \ }) diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c index fa64115cd7c7..0074b600a0ef 100644 --- a/drivers/isdn/hisax/st5481_b.c +++ b/drivers/isdn/hisax/st5481_b.c @@ -180,7 +180,7 @@ static void usb_b_out_complete(struct urb *urb) DBG(4,"urb killed status %d", urb->status); return; // Give up default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); if (b_out->busy == 0) { st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2 | USB_DIR_OUT, NULL, NULL); } @@ -372,6 +372,6 @@ void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg) B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL); break; default: - WARN("pr %#x\n", pr); + WARNING("pr %#x\n", pr); } } diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c index b8c4855cc889..077991c1cd05 100644 --- a/drivers/isdn/hisax/st5481_d.c +++ b/drivers/isdn/hisax/st5481_d.c @@ -389,7 +389,7 @@ static void usb_d_out_complete(struct urb *urb) DBG(1,"urb killed status %d", urb->status); break; default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); if (d_out->busy == 0) { st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter); } @@ -420,7 +420,7 @@ static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg) isdnhdlc_out_init(&d_out->hdlc_state, 1, 0); if (test_and_set_bit(buf_nr, &d_out->busy)) { - WARN("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy); + WARNING("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy); return; } urb = d_out->urb[buf_nr]; @@ -601,7 +601,7 @@ void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg) FsmEvent(&adapter->d_out.fsm, EV_DOUT_START_XMIT, NULL); break; default: - WARN("pr %#x\n", pr); + WARNING("pr %#x\n", pr); break; } } diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c index 427a8b0520f5..ec3c0e507669 100644 --- a/drivers/isdn/hisax/st5481_usb.c +++ b/drivers/isdn/hisax/st5481_usb.c @@ -66,7 +66,7 @@ static void usb_ctrl_msg(struct st5481_adapter *adapter, struct ctrl_msg *ctrl_msg; if ((w_index = fifo_add(&ctrl->msg_fifo.f)) < 0) { - WARN("control msg FIFO full"); + WARNING("control msg FIFO full"); return; } ctrl_msg = &ctrl->msg_fifo.data[w_index]; @@ -139,7 +139,7 @@ static void usb_ctrl_complete(struct urb *urb) DBG(1,"urb killed status %d", urb->status); return; // Give up default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); break; } } @@ -198,7 +198,7 @@ static void usb_int_complete(struct urb *urb) DBG(2, "urb shutting down with status: %d", urb->status); return; default: - WARN("nonzero urb status received: %d", urb->status); + WARNING("nonzero urb status received: %d", urb->status); goto exit; } @@ -235,7 +235,7 @@ static void usb_int_complete(struct urb *urb) exit: status = usb_submit_urb (urb, GFP_ATOMIC); if (status) - WARN("usb_submit_urb failed with result %d", status); + WARNING("usb_submit_urb failed with result %d", status); } /* ====================================================================== @@ -257,7 +257,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter) DBG(2,""); if ((status = usb_reset_configuration (dev)) < 0) { - WARN("reset_configuration failed,status=%d",status); + WARNING("reset_configuration failed,status=%d",status); return status; } @@ -269,7 +269,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter) // Check if the config is sane if ( altsetting->desc.bNumEndpoints != 7 ) { - WARN("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints); + WARNING("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints); return -EINVAL; } @@ -279,7 +279,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter) // Use alternative setting 3 on interface 0 to have 2B+D if ((status = usb_set_interface (dev, 0, 3)) < 0) { - WARN("usb_set_interface failed,status=%d",status); + WARNING("usb_set_interface failed,status=%d",status); return status; } @@ -497,7 +497,7 @@ static void usb_in_complete(struct urb *urb) DBG(1,"urb killed status %d", urb->status); return; // Give up default: - WARN("urb status %d",urb->status); + WARNING("urb status %d",urb->status); break; } } @@ -523,7 +523,7 @@ static void usb_in_complete(struct urb *urb) DBG(4,"count=%d",status); DBG_PACKET(0x400, in->rcvbuf, status); if (!(skb = dev_alloc_skb(status))) { - WARN("receive out of memory\n"); + WARNING("receive out of memory\n"); break; } memcpy(skb_put(skb, status), in->rcvbuf, status); diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h index a973f2a50fb9..c65d62295890 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -171,7 +171,7 @@ struct at91_request { #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) #define DBG(stuff...) pr_debug("udc: " stuff) diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index d490d0289507..a39a4b940c33 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -170,7 +170,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) * but if the controller isn't recognized at all then * that assumption is a bit more likely to be wrong. */ - WARN(cdev, "controller '%s' not recognized; trying %s\n", + WARNING(cdev, "controller '%s' not recognized; trying %s\n", gadget->name, cdc_config_driver.label); device_desc.bcdDevice = diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index d7aaaa29b1e1..bcac2e68660d 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -293,7 +293,7 @@ static int __init eth_bind(struct usb_composite_dev *cdev) * but if the controller isn't recognized at all then * that assumption is a bit more likely to be wrong. */ - WARN(cdev, "controller '%s' not recognized; trying %s\n", + WARNING(cdev, "controller '%s' not recognized; trying %s\n", gadget->name, eth_config_driver.label); device_desc.bcdDevice = diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 15c24edbb61a..ea2c31d18080 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -308,7 +308,7 @@ MODULE_LICENSE("Dual BSD/GPL"); dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ +#define WARNING(d, fmt, args...) \ dev_warn(&(d)->gadget->dev , fmt , ## args) #define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args) @@ -1091,7 +1091,7 @@ static int ep0_queue(struct fsg_dev *fsg) if (rc != 0 && rc != -ESHUTDOWN) { /* We can't do much more than wait for a reset */ - WARN(fsg, "error in submission: %s --> %d\n", + WARNING(fsg, "error in submission: %s --> %d\n", fsg->ep0->name, rc); } return rc; @@ -1227,7 +1227,7 @@ static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) /* Save the command for later */ if (fsg->cbbuf_cmnd_size) - WARN(fsg, "CB[I] overwriting previous command\n"); + WARNING(fsg, "CB[I] overwriting previous command\n"); fsg->cbbuf_cmnd_size = req->actual; memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); @@ -1506,7 +1506,7 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, * submissions if DMA is enabled. */ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0)) - WARN(fsg, "error in submission: %s --> %d\n", + WARNING(fsg, "error in submission: %s --> %d\n", ep->name, rc); } } @@ -2294,7 +2294,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg) VDBG(fsg, "delayed bulk-in endpoint halt\n"); while (rc != 0) { if (rc != -EAGAIN) { - WARN(fsg, "usb_ep_set_halt -> %d\n", rc); + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); rc = 0; break; } @@ -2317,7 +2317,7 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) VDBG(fsg, "delayed bulk-in endpoint wedge\n"); while (rc != 0) { if (rc != -EAGAIN) { - WARN(fsg, "usb_ep_set_wedge -> %d\n", rc); + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); rc = 0; break; } @@ -3755,7 +3755,7 @@ static int __init check_parameters(struct fsg_dev *fsg) if (gcnum >= 0) mod_data.release = 0x0300 + gcnum; else { - WARN(fsg, "controller '%s' not recognized\n", + WARNING(fsg, "controller '%s' not recognized\n", fsg->gadget->name); mod_data.release = 0x0399; } diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c index 1695382f30fe..1cfccf102a2d 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -1538,7 +1538,7 @@ static void dtd_complete_irq(struct fsl_udc *udc) /* If the ep is configured */ if (curr_ep->name == NULL) { - WARN("Invalid EP?"); + WARNING("Invalid EP?"); continue; } diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index 98b1483ef6a5..6131752a38bc 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -552,7 +552,7 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length) #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 7f4d4828e3aa..ea8651e3da1a 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -138,8 +138,6 @@ static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req); dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ - dev_warn(&(d)->gadget->dev , fmt , ## args) #define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args) diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 48f1c63b7013..60aa04847b18 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1768,7 +1768,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) * usb_gadget_driver_{register,unregister}() must change. */ if (the_controller) { - WARN(dev, "ignoring %s\n", pci_name(pdev)); + WARNING(dev, "ignoring %s\n", pci_name(pdev)); return -EBUSY; } if (!pdev->irq) { diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h index bc4eb1e0b507..566cb2319056 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/goku_udc.h @@ -285,7 +285,7 @@ struct goku_udc { #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ +#define WARNING(dev,fmt,args...) \ xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 04692d59fc1c..f4585d3e90d7 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -262,8 +262,6 @@ static const char *CHIP; #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index b67ab677af72..5cfb5ebf3881 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -1007,7 +1007,7 @@ static void scan_dma_completions (struct net2280_ep *ep) * 0122, and 0124; not all cases trigger the warning. */ if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - WARN (ep->dev, "%s lost packet sync!\n", + WARNING (ep->dev, "%s lost packet sync!\n", ep->ep.name); req->req.status = -EOVERFLOW; } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) { diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index 1f2af398a9a4..81a71dbdc2c6 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -272,7 +272,7 @@ static inline void net2280_led_shutdown (struct net2280 *dev) #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev,fmt,args...) \ +#define WARNING(dev,fmt,args...) \ xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 4b79a8509e84..395bd1844482 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -1120,7 +1120,7 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value) status = -EINVAL; else if (value) { if (ep->udc->ep0_set_config) { - WARN("error changing config?\n"); + WARNING("error changing config?\n"); omap_writew(UDC_CLR_CFG, UDC_SYSCON2); } omap_writew(UDC_STALL_CMD, UDC_SYSCON2); @@ -1764,7 +1764,7 @@ do_stall: u.r.bRequestType, u.r.bRequest, status); if (udc->ep0_set_config) { if (udc->ep0_reset_config) - WARN("error resetting config?\n"); + WARNING("error resetting config?\n"); else omap_writew(UDC_CLR_CFG, UDC_SYSCON2); } @@ -3076,7 +3076,7 @@ static int omap_udc_suspend(struct platform_device *dev, pm_message_t message) * which would prevent entry to deep sleep... */ if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) { - WARN("session active; suspend requires disconnect\n"); + WARNING("session active; suspend requires disconnect\n"); omap_pullup(&udc->gadget, 0); } diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index 8522bbb12278..29edc51b6b22 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -188,7 +188,7 @@ struct omap_udc { #endif #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) #define DBG(stuff...) pr_debug("udc: " stuff) diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 49cd9e145a9b..e0090085b78e 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -179,7 +179,7 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); #define ERROR(dev, fmt, args...) \ xprintk(dev, KERN_ERR, fmt, ## args) -#define WARN(dev, fmt, args...) \ +#define WARNING(dev, fmt, args...) \ xprintk(dev, KERN_WARNING, fmt, ## args) #define INFO(dev, fmt, args...) \ xprintk(dev, KERN_INFO, fmt, ## args) diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 8fb0066609bb..7e6725d89976 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -342,7 +342,7 @@ pxa25x_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) struct pxa25x_request *req; req = container_of (_req, struct pxa25x_request, req); - WARN_ON (!list_empty (&req->queue)); + WARN_ON(!list_empty (&req->queue)); kfree(req); } @@ -1556,7 +1556,7 @@ config_change: * tell us about config change events, * so later ones may fail... */ - WARN("config change %02x fail %d?\n", + WARNING("config change %02x fail %d?\n", u.r.bRequest, i); return; /* TODO experiment: if has_cfr, @@ -2330,7 +2330,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) unsigned long flags; if (!udc->mach->gpio_pullup && !udc->mach->udc_command) - WARN("USB host won't detect disconnect!\n"); + WARNING("USB host won't detect disconnect!\n"); udc->suspended = 1; local_irq_save(flags); diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h index 4d11ece7c95f..c8a13215e02c 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -259,7 +259,7 @@ dump_state(struct pxa25x_udc *dev) #define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) #define ERR(stuff...) pr_err("udc: " stuff) -#define WARN(stuff...) pr_warning("udc: " stuff) +#define WARNING(stuff...) pr_warning("udc: " stuff) #define INFO(stuff...) pr_info("udc: " stuff) diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 5458f43a8668..3791e6271903 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -116,7 +116,6 @@ static inline int qlen(struct usb_gadget *gadget) #undef DBG #undef VDBG #undef ERROR -#undef WARN #undef INFO #define xprintk(d, level, fmt, args...) \ @@ -140,8 +139,6 @@ static inline int qlen(struct usb_gadget *gadget) #define ERROR(dev, fmt, args...) \ xprintk(dev , KERN_ERR , fmt , ## args) -#define WARN(dev, fmt, args...) \ - xprintk(dev , KERN_WARNING , fmt , ## args) #define INFO(dev, fmt, args...) \ xprintk(dev , KERN_INFO , fmt , ## args) diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 31178e10cbbe..ce1ca0ba0515 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -882,7 +882,7 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd, for (i = 0; i < 100 && !list_empty(&hep->urb_list); i++) msleep(3); if (!list_empty(&hep->urb_list)) - WARN("ep %p not empty?\n", ep); + WARNING("ep %p not empty?\n", ep); kfree(ep); hep->hcpriv = NULL; diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h index 595b90a99848..aa211bafcff9 100644 --- a/drivers/usb/host/isp116x.h +++ b/drivers/usb/host/isp116x.h @@ -338,7 +338,7 @@ struct isp116x_ep { #endif #define ERR(stuff...) printk(KERN_ERR "116x: " stuff) -#define WARN(stuff...) printk(KERN_WARNING "116x: " stuff) +#define WARNING(stuff...) printk(KERN_WARNING "116x: " stuff) #define INFO(stuff...) printk(KERN_INFO "116x: " stuff) /* ------------------------------------------------- */ diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 340d72da554a..8a74bbb57d08 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1026,7 +1026,7 @@ sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) if (!list_empty(&hep->urb_list)) msleep(3); if (!list_empty(&hep->urb_list)) - WARN("ep %p not empty?\n", ep); + WARNING("ep %p not empty?\n", ep); kfree(ep); hep->hcpriv = NULL; diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h index 7690d98e42a7..b6b8c1f233dd 100644 --- a/drivers/usb/host/sl811.h +++ b/drivers/usb/host/sl811.h @@ -261,6 +261,6 @@ sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count) #endif #define ERR(stuff...) printk(KERN_ERR "sl811: " stuff) -#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff) +#define WARNING(stuff...) printk(KERN_WARNING "sl811: " stuff) #define INFO(stuff...) printk(KERN_INFO "sl811: " stuff) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 054dedd28127..b358c4e1cf21 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -81,7 +81,7 @@ static struct usb_device *testdev_to_usbdev (struct usbtest_dev *test) #define ERROR(tdev, fmt, args...) \ dev_err(&(tdev)->intf->dev , fmt , ## args) -#define WARN(tdev, fmt, args...) \ +#define WARNING(tdev, fmt, args...) \ dev_warn(&(tdev)->intf->dev , fmt , ## args) /*-------------------------------------------------------------------------*/ @@ -1946,7 +1946,7 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id) status = get_endpoints (dev, intf); if (status < 0) { - WARN(dev, "couldn't get endpoints, %d\n", + WARNING(dev, "couldn't get endpoints, %d\n", status); return status; } diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 747c3a49cdc9..c932390c6da0 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -330,7 +330,7 @@ extern int usb_string_id(struct usb_composite_dev *c); dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) \ dev_err(&(d)->gadget->dev , fmt , ## args) -#define WARN(d, fmt, args...) \ +#define WARNING(d, fmt, args...) \ dev_warn(&(d)->gadget->dev , fmt , ## args) #define INFO(d, fmt, args...) \ dev_info(&(d)->gadget->dev , fmt , ## args) -- cgit From 2711b793eb62a5873a0ba583a69252040aef176e Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Fri, 25 Jul 2008 01:45:56 -0700 Subject: kallsyms: unify 32- and 64-bit code Use the %p format string which already accounts for the padding you need with a pointer type on a particular architecture. Also replace the macro with a static inline function to match the rest of the file. Cc: Heiko Carstens Cc: Arjan van de Ven Signed-off-by: Vegard Nossum Cc: Sam Ravnborg Cc: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kallsyms.h | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h index 00c1801099fa..57aefa160a92 100644 --- a/include/linux/kallsyms.h +++ b/include/linux/kallsyms.h @@ -6,6 +6,7 @@ #define _LINUX_KALLSYMS_H #include +#include #include #define KSYM_NAME_LEN 128 @@ -105,18 +106,10 @@ static inline void print_fn_descriptor_symbol(const char *fmt, void *addr) print_symbol(fmt, (unsigned long)addr); } -#ifndef CONFIG_64BIT -#define print_ip_sym(ip) \ -do { \ - printk("[<%08lx>]", ip); \ - print_symbol(" %s\n", ip); \ -} while(0) -#else -#define print_ip_sym(ip) \ -do { \ - printk("[<%016lx>]", ip); \ - print_symbol(" %s\n", ip); \ -} while(0) -#endif +static inline void print_ip_sym(unsigned long ip) +{ + printk("[<%p>]", (void *) ip); + print_symbol(" %s\n", ip); +} #endif /*_LINUX_KALLSYMS_H*/ -- cgit From 717115e1a5856b57af0f71e1df7149108294fc10 Mon Sep 17 00:00:00 2001 From: Dave Young Date: Fri, 25 Jul 2008 01:45:58 -0700 Subject: printk ratelimiting rewrite All ratelimit user use same jiffies and burst params, so some messages (callbacks) will be lost. For example: a call printk_ratelimit(5 * HZ, 1) b call printk_ratelimit(5 * HZ, 1) before the 5*HZ timeout of a, then b will will be supressed. - rewrite __ratelimit, and use a ratelimit_state as parameter. Thanks for hints from andrew. - Add WARN_ON_RATELIMIT, update rcupreempt.h - remove __printk_ratelimit - use __ratelimit in net_ratelimit Signed-off-by: Dave Young Cc: "David S. Miller" Cc: "Paul E. McKenney" Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/bug.h | 3 +++ include/linux/kernel.h | 8 ++----- include/linux/net.h | 3 +-- include/linux/ratelimit.h | 27 +++++++++++++++++++++++ include/linux/rcupreempt.h | 9 ++++++-- kernel/printk.c | 17 +++----------- kernel/sysctl.c | 4 ++-- lib/ratelimit.c | 55 +++++++++++++++++++++++++--------------------- net/core/sysctl_net_core.c | 4 ++-- net/core/utils.c | 5 ++--- 10 files changed, 79 insertions(+), 56 deletions(-) create mode 100644 include/linux/ratelimit.h (limited to 'include/linux') diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h index a346e744e770..a3f738cffdb6 100644 --- a/include/asm-generic/bug.h +++ b/include/asm-generic/bug.h @@ -97,6 +97,9 @@ extern void warn_slowpath(const char *file, const int line, unlikely(__ret_warn_once); \ }) +#define WARN_ON_RATELIMIT(condition, state) \ + WARN_ON((condition) && __ratelimit(state)) + #ifdef CONFIG_SMP # define WARN_ON_SMP(x) WARN_ON(x) #else diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 5c4b1251e110..fdbbf72ca2eb 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -189,11 +190,8 @@ asmlinkage int vprintk(const char *fmt, va_list args) asmlinkage int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))) __cold; -extern int printk_ratelimit_jiffies; -extern int printk_ratelimit_burst; +extern struct ratelimit_state printk_ratelimit_state; extern int printk_ratelimit(void); -extern int __ratelimit(int ratelimit_jiffies, int ratelimit_burst); -extern int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst); extern bool printk_timed_ratelimit(unsigned long *caller_jiffies, unsigned int interval_msec); #else @@ -204,8 +202,6 @@ static inline int printk(const char *s, ...) __attribute__ ((format (printf, 1, 2))); static inline int __cold printk(const char *s, ...) { return 0; } static inline int printk_ratelimit(void) { return 0; } -static inline int __printk_ratelimit(int ratelimit_jiffies, \ - int ratelimit_burst) { return 0; } static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \ unsigned int interval_msec) \ { return false; } diff --git a/include/linux/net.h b/include/linux/net.h index 2f999fbb188d..4a9a30f2d68f 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -351,8 +351,7 @@ static const struct proto_ops name##_ops = { \ #ifdef CONFIG_SYSCTL #include -extern int net_msg_cost; -extern int net_msg_burst; +extern struct ratelimit_state net_ratelimit_state; #endif #endif /* __KERNEL__ */ diff --git a/include/linux/ratelimit.h b/include/linux/ratelimit.h new file mode 100644 index 000000000000..18a5b9ba9d40 --- /dev/null +++ b/include/linux/ratelimit.h @@ -0,0 +1,27 @@ +#ifndef _LINUX_RATELIMIT_H +#define _LINUX_RATELIMIT_H +#include + +#define DEFAULT_RATELIMIT_INTERVAL (5 * HZ) +#define DEFAULT_RATELIMIT_BURST 10 + +struct ratelimit_state { + int interval; + int burst; + int printed; + int missed; + unsigned long begin; +}; + +#define DEFINE_RATELIMIT_STATE(name, interval, burst) \ + struct ratelimit_state name = {interval, burst,} + +extern int __ratelimit(struct ratelimit_state *rs); + +static inline int ratelimit(void) +{ + static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + return __ratelimit(&rs); +} +#endif diff --git a/include/linux/rcupreempt.h b/include/linux/rcupreempt.h index f04b64eca636..0967f03b0705 100644 --- a/include/linux/rcupreempt.h +++ b/include/linux/rcupreempt.h @@ -115,16 +115,21 @@ DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched); static inline void rcu_enter_nohz(void) { + static DEFINE_RATELIMIT_STATE(rs, 10 * HZ, 1); + smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */ __get_cpu_var(rcu_dyntick_sched).dynticks++; - WARN_ON(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1); + WARN_ON_RATELIMIT(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1, &rs); } static inline void rcu_exit_nohz(void) { + static DEFINE_RATELIMIT_STATE(rs, 10 * HZ, 1); + smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ __get_cpu_var(rcu_dyntick_sched).dynticks++; - WARN_ON(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1)); + WARN_ON_RATELIMIT(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1), + &rs); } #else /* CONFIG_NO_HZ */ diff --git a/kernel/printk.c b/kernel/printk.c index 3f7a2a94583b..a7f7559c5f6c 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1308,6 +1308,8 @@ void tty_write_message(struct tty_struct *tty, char *msg) } #if defined CONFIG_PRINTK + +DEFINE_RATELIMIT_STATE(printk_ratelimit_state, 5 * HZ, 10); /* * printk rate limiting, lifted from the networking subsystem. * @@ -1315,22 +1317,9 @@ void tty_write_message(struct tty_struct *tty, char *msg) * every printk_ratelimit_jiffies to make a denial-of-service * attack impossible. */ -int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst) -{ - return __ratelimit(ratelimit_jiffies, ratelimit_burst); -} -EXPORT_SYMBOL(__printk_ratelimit); - -/* minimum time in jiffies between messages */ -int printk_ratelimit_jiffies = 5 * HZ; - -/* number of messages we send before ratelimiting */ -int printk_ratelimit_burst = 10; - int printk_ratelimit(void) { - return __printk_ratelimit(printk_ratelimit_jiffies, - printk_ratelimit_burst); + return __ratelimit(&printk_ratelimit_state); } EXPORT_SYMBOL(printk_ratelimit); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 1a8299d1fe59..35a50db9b6ce 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -624,7 +624,7 @@ static struct ctl_table kern_table[] = { { .ctl_name = KERN_PRINTK_RATELIMIT, .procname = "printk_ratelimit", - .data = &printk_ratelimit_jiffies, + .data = &printk_ratelimit_state.interval, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, @@ -633,7 +633,7 @@ static struct ctl_table kern_table[] = { { .ctl_name = KERN_PRINTK_RATELIMIT_BURST, .procname = "printk_ratelimit_burst", - .data = &printk_ratelimit_burst, + .data = &printk_ratelimit_state.burst, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec, diff --git a/lib/ratelimit.c b/lib/ratelimit.c index 485e3040dcd4..35136671b215 100644 --- a/lib/ratelimit.c +++ b/lib/ratelimit.c @@ -3,6 +3,9 @@ * * Isolated from kernel/printk.c by Dave Young * + * 2008-05-01 rewrite the function and use a ratelimit_state data struct as + * parameter. Now every user can use their own standalone ratelimit_state. + * * This file is released under the GPLv2. * */ @@ -11,41 +14,43 @@ #include #include +static DEFINE_SPINLOCK(ratelimit_lock); +static unsigned long flags; + /* * __ratelimit - rate limiting - * @ratelimit_jiffies: minimum time in jiffies between two callbacks - * @ratelimit_burst: number of callbacks we do before ratelimiting + * @rs: ratelimit_state data * - * This enforces a rate limit: not more than @ratelimit_burst callbacks - * in every ratelimit_jiffies + * This enforces a rate limit: not more than @rs->ratelimit_burst callbacks + * in every @rs->ratelimit_jiffies */ -int __ratelimit(int ratelimit_jiffies, int ratelimit_burst) +int __ratelimit(struct ratelimit_state *rs) { - static DEFINE_SPINLOCK(ratelimit_lock); - static unsigned toks = 10 * 5 * HZ; - static unsigned long last_msg; - static int missed; - unsigned long flags; - unsigned long now = jiffies; + if (!rs->interval) + return 1; spin_lock_irqsave(&ratelimit_lock, flags); - toks += now - last_msg; - last_msg = now; - if (toks > (ratelimit_burst * ratelimit_jiffies)) - toks = ratelimit_burst * ratelimit_jiffies; - if (toks >= ratelimit_jiffies) { - int lost = missed; + if (!rs->begin) + rs->begin = jiffies; - missed = 0; - toks -= ratelimit_jiffies; - spin_unlock_irqrestore(&ratelimit_lock, flags); - if (lost) - printk(KERN_WARNING "%s: %d messages suppressed\n", - __func__, lost); - return 1; + if (time_is_before_jiffies(rs->begin + rs->interval)) { + if (rs->missed) + printk(KERN_WARNING "%s: %d callbacks suppressed\n", + __func__, rs->missed); + rs->begin = 0; + rs->printed = 0; + rs->missed = 0; } - missed++; + if (rs->burst && rs->burst > rs->printed) + goto print; + + rs->missed++; spin_unlock_irqrestore(&ratelimit_lock, flags); return 0; + +print: + rs->printed++; + spin_unlock_irqrestore(&ratelimit_lock, flags); + return 1; } EXPORT_SYMBOL(__ratelimit); diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index a570e2af22cb..f686467ff12b 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -67,7 +67,7 @@ static struct ctl_table net_core_table[] = { { .ctl_name = NET_CORE_MSG_COST, .procname = "message_cost", - .data = &net_msg_cost, + .data = &net_ratelimit_state.interval, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, @@ -76,7 +76,7 @@ static struct ctl_table net_core_table[] = { { .ctl_name = NET_CORE_MSG_BURST, .procname = "message_burst", - .data = &net_msg_burst, + .data = &net_ratelimit_state.burst, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec, diff --git a/net/core/utils.c b/net/core/utils.c index 8031eb59054e..72e0ebe964a0 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -31,17 +31,16 @@ #include #include -int net_msg_cost __read_mostly = 5*HZ; -int net_msg_burst __read_mostly = 10; int net_msg_warn __read_mostly = 1; EXPORT_SYMBOL(net_msg_warn); +DEFINE_RATELIMIT_STATE(net_ratelimit_state, 5 * HZ, 10); /* * All net warning printk()s should be guarded by this function. */ int net_ratelimit(void) { - return __printk_ratelimit(net_msg_cost, net_msg_burst); + return __ratelimit(&net_ratelimit_state); } EXPORT_SYMBOL(net_ratelimit); -- cgit From 472dba7d117844c746be97db6be26c2810d79b62 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 25 Jul 2008 01:45:58 -0700 Subject: sm501: add power control callback Add callback to get or set the power control if the device has the sleep connected to some form of GPIO. Signed-off-by: Ben Dooks Cc: Arnaud Patard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mfd/sm501.c | 31 +++++++++++++++++++++++++++++++ include/linux/sm501.h | 7 +++++++ 2 files changed, 38 insertions(+) (limited to 'include/linux') diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index e2530df4d85c..9296b2673b52 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1138,8 +1138,31 @@ static int sm501_plat_probe(struct platform_device *dev) } #ifdef CONFIG_PM + /* power management support */ +static void sm501_set_power(struct sm501_devdata *sm, int on) +{ + struct sm501_platdata *pd = sm->platdata; + + if (pd == NULL) + return; + + if (pd->get_power) { + if (pd->get_power(sm->dev) == on) { + dev_dbg(sm->dev, "is already %d\n", on); + return; + } + } + + if (pd->set_power) { + dev_dbg(sm->dev, "setting power to %d\n", on); + + pd->set_power(sm->dev, on); + sm501_mdelay(sm, 10); + } +} + static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state) { struct sm501_devdata *sm = platform_get_drvdata(pdev); @@ -1148,6 +1171,12 @@ static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state) sm->pm_misc = readl(sm->regs + SM501_MISC_CONTROL); sm501_dump_regs(sm); + + if (sm->platdata) { + if (sm->platdata->flags & SM501_FLAG_SUSPEND_OFF) + sm501_set_power(sm, 0); + } + return 0; } @@ -1155,6 +1184,8 @@ static int sm501_plat_resume(struct platform_device *pdev) { struct sm501_devdata *sm = platform_get_drvdata(pdev); + sm501_set_power(sm, 1); + sm501_dump_regs(sm); sm501_dump_gate(sm); sm501_dump_clk(sm); diff --git a/include/linux/sm501.h b/include/linux/sm501.h index b530fa6a1d34..145405bf9efa 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -157,6 +157,8 @@ struct sm501_init_gpio { struct sm501_reg_init gpio_ddr_high; }; +#define SM501_FLAG_SUSPEND_OFF (1<<4) + /* sm501_platdata * * This is passed with the platform device to allow the board @@ -170,6 +172,11 @@ struct sm501_platdata { struct sm501_init_gpio *init_gpiop; struct sm501_platdata_fb *fb; + int flags; + + int (*get_power)(struct device *dev); + int (*set_power)(struct device *dev, unsigned int on); + struct sm501_platdata_gpio_i2c *gpio_i2c; unsigned int gpio_i2c_nr; }; -- cgit From f61be273d3699d174bc1438e6804f9f9e52bb932 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 25 Jul 2008 01:45:59 -0700 Subject: sm501: add gpiolib support Add support for exporting the GPIOs on the SM501 via gpiolib. Signed-off-by: Ben Dooks Cc: Arnaud Patard Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mfd/Kconfig | 8 ++ drivers/mfd/sm501.c | 299 +++++++++++++++++++++++++++++++++++++++++--------- include/linux/sm501.h | 20 +--- 3 files changed, 257 insertions(+), 70 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9f93c29fed35..bac9e973ece0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -19,6 +19,14 @@ config MFD_SM501 interface. The device may be connected by PCI or local bus with varying functions enabled. +config MFD_SM501_GPIO + bool "Export GPIO via GPIO layer" + depends on MFD_SM501 && HAVE_GPIO_LIB + ---help--- + This option uses the gpio library layer to export the 64 GPIO + lines on the SM501. The platform data is used to supply the + base number for the first GPIO line to register. + config MFD_ASIC3 bool "Support for Compaq ASIC3" depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB && ARM diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 9296b2673b52..be8713908125 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -31,10 +32,29 @@ struct sm501_device { struct platform_device pdev; }; +struct sm501_gpio; + +struct sm501_gpio_chip { + struct gpio_chip gpio; + struct sm501_gpio *ourgpio; /* to get back to parent. */ + void __iomem *regbase; +}; + +struct sm501_gpio { + struct sm501_gpio_chip low; + struct sm501_gpio_chip high; + spinlock_t lock; + + unsigned int registered : 1; + void __iomem *regs; + struct resource *regs_res; +}; + struct sm501_devdata { spinlock_t reg_lock; struct mutex clock_lock; struct list_head devices; + struct sm501_gpio gpio; struct device *dev; struct resource *io_res; @@ -42,6 +62,7 @@ struct sm501_devdata { struct resource *regs_claim; struct sm501_platdata *platdata; + unsigned int in_suspend; unsigned long pm_misc; @@ -52,6 +73,7 @@ struct sm501_devdata { unsigned int rev; }; + #define MHZ (1000 * 1000) #ifdef DEBUG @@ -276,58 +298,6 @@ unsigned long sm501_modify_reg(struct device *dev, EXPORT_SYMBOL_GPL(sm501_modify_reg); -unsigned long sm501_gpio_get(struct device *dev, - unsigned long gpio) -{ - struct sm501_devdata *sm = dev_get_drvdata(dev); - unsigned long result; - unsigned long reg; - - reg = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; - result = readl(sm->regs + reg); - - result >>= (gpio & 31); - return result & 1UL; -} - -EXPORT_SYMBOL_GPL(sm501_gpio_get); - -void sm501_gpio_set(struct device *dev, - unsigned long gpio, - unsigned int to, - unsigned int dir) -{ - struct sm501_devdata *sm = dev_get_drvdata(dev); - - unsigned long bit = 1 << (gpio & 31); - unsigned long base; - unsigned long save; - unsigned long val; - - base = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; - base += SM501_GPIO; - - spin_lock_irqsave(&sm->reg_lock, save); - - val = readl(sm->regs + base) & ~bit; - if (to) - val |= bit; - writel(val, sm->regs + base); - - val = readl(sm->regs + SM501_GPIO_DDR_LOW) & ~bit; - if (dir) - val |= bit; - - writel(val, sm->regs + SM501_GPIO_DDR_LOW); - sm501_sync_regs(sm); - - spin_unlock_irqrestore(&sm->reg_lock, save); - -} - -EXPORT_SYMBOL_GPL(sm501_gpio_set); - - /* sm501_unit_power * * alters the power active gate to set specific units on or off @@ -906,6 +876,226 @@ static int sm501_register_display(struct sm501_devdata *sm, return sm501_register_device(sm, pdev); } +#ifdef CONFIG_MFD_SM501_GPIO + +static inline struct sm501_gpio_chip *to_sm501_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct sm501_gpio_chip, gpio); +} + +static inline struct sm501_devdata *sm501_gpio_to_dev(struct sm501_gpio *gpio) +{ + return container_of(gpio, struct sm501_devdata, gpio); +} + +static int sm501_gpio_get(struct gpio_chip *chip, unsigned offset) + +{ + struct sm501_gpio_chip *smgpio = to_sm501_gpio(chip); + unsigned long result; + + result = readl(smgpio->regbase + SM501_GPIO_DATA_LOW); + result >>= offset; + + return result & 1UL; +} + +static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value) + +{ + struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); + struct sm501_gpio *smgpio = smchip->ourgpio; + unsigned long bit = 1 << offset; + void __iomem *regs = smchip->regbase; + unsigned long save; + unsigned long val; + + dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", + __func__, chip, offset); + + spin_lock_irqsave(&smgpio->lock, save); + + val = readl(regs + SM501_GPIO_DATA_LOW) & ~bit; + if (value) + val |= bit; + writel(val, regs); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + spin_unlock_irqrestore(&smgpio->lock, save); +} + +static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset) +{ + struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); + struct sm501_gpio *smgpio = smchip->ourgpio; + void __iomem *regs = smchip->regbase; + unsigned long bit = 1 << offset; + unsigned long save; + unsigned long ddr; + + dev_info(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n", + __func__, chip, offset); + + spin_lock_irqsave(&smgpio->lock, save); + + ddr = readl(regs + SM501_GPIO_DDR_LOW); + writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + spin_unlock_irqrestore(&smgpio->lock, save); + + return 0; +} + +static int sm501_gpio_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct sm501_gpio_chip *smchip = to_sm501_gpio(chip); + struct sm501_gpio *smgpio = smchip->ourgpio; + unsigned long bit = 1 << offset; + void __iomem *regs = smchip->regbase; + unsigned long save; + unsigned long val; + unsigned long ddr; + + dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d,%d)\n", + __func__, chip, offset, value); + + spin_lock_irqsave(&smgpio->lock, save); + + val = readl(regs + SM501_GPIO_DATA_LOW); + if (value) + val |= bit; + else + val &= ~bit; + writel(val, regs); + + ddr = readl(regs + SM501_GPIO_DDR_LOW); + writel(ddr | bit, regs + SM501_GPIO_DDR_LOW); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + writel(val, regs + SM501_GPIO_DATA_LOW); + + sm501_sync_regs(sm501_gpio_to_dev(smgpio)); + spin_unlock_irqrestore(&smgpio->lock, save); + + return 0; +} + +static struct gpio_chip gpio_chip_template = { + .ngpio = 32, + .direction_input = sm501_gpio_input, + .direction_output = sm501_gpio_output, + .set = sm501_gpio_set, + .get = sm501_gpio_get, +}; + +static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm, + struct sm501_gpio *gpio, + struct sm501_gpio_chip *chip) +{ + struct sm501_platdata *pdata = sm->platdata; + struct gpio_chip *gchip = &chip->gpio; + unsigned base = pdata->gpio_base; + + memcpy(chip, &gpio_chip_template, sizeof(struct gpio_chip)); + + if (chip == &gpio->high) { + base += 32; + chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH; + gchip->label = "SM501-HIGH"; + } else { + chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW; + gchip->label = "SM501-LOW"; + } + + gchip->base = base; + chip->ourgpio = gpio; + + return gpiochip_add(gchip); +} + +static int sm501_register_gpio(struct sm501_devdata *sm) +{ + struct sm501_gpio *gpio = &sm->gpio; + resource_size_t iobase = sm->io_res->start + SM501_GPIO; + int ret; + int tmp; + + dev_dbg(sm->dev, "registering gpio block %08llx\n", + (unsigned long long)iobase); + + spin_lock_init(&gpio->lock); + + gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio"); + if (gpio->regs_res == NULL) { + dev_err(sm->dev, "gpio: failed to request region\n"); + return -ENXIO; + } + + gpio->regs = ioremap(iobase, 0x20); + if (gpio->regs == NULL) { + dev_err(sm->dev, "gpio: failed to remap registers\n"); + ret = -ENXIO; + goto err_mapped; + } + + /* Register both our chips. */ + + ret = sm501_gpio_register_chip(sm, gpio, &gpio->low); + if (ret) { + dev_err(sm->dev, "failed to add low chip\n"); + goto err_mapped; + } + + ret = sm501_gpio_register_chip(sm, gpio, &gpio->high); + if (ret) { + dev_err(sm->dev, "failed to add high chip\n"); + goto err_low_chip; + } + + gpio->registered = 1; + + return 0; + + err_low_chip: + tmp = gpiochip_remove(&gpio->low.gpio); + if (tmp) { + dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n"); + return ret; + } + + err_mapped: + release_resource(gpio->regs_res); + kfree(gpio->regs_res); + + return ret; +} + +static void sm501_gpio_remove(struct sm501_devdata *sm) +{ + int ret; + + ret = gpiochip_remove(&sm->gpio.low.gpio); + if (ret) + dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n"); + + ret = gpiochip_remove(&sm->gpio.high.gpio); + if (ret) + dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n"); +} + +#else +static int sm501_register_gpio(struct sm501_devdata *sm) +{ + return 0; +} + +static void sm501_gpio_remove(struct sm501_devdata *sm) +{ +} +#endif + /* sm501_dbg_regs * * Debug attribute to attach to parent device to show core registers @@ -1059,6 +1249,8 @@ static int sm501_init_dev(struct sm501_devdata *sm) sm501_register_usbhost(sm, &mem_avail); if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1)) sm501_register_uart(sm, idata->devices); + if (idata->devices & SM501_USE_GPIO) + sm501_register_gpio(sm); } ret = sm501_check_clocks(sm); @@ -1366,6 +1558,9 @@ static void sm501_dev_remove(struct sm501_devdata *sm) sm501_remove_sub(sm, smdev); device_remove_file(sm->dev, &dev_attr_dbg_regs); + + if (sm->gpio.registered) + sm501_gpio_remove(sm); } static void sm501_pci_remove(struct pci_dev *dev) diff --git a/include/linux/sm501.h b/include/linux/sm501.h index 145405bf9efa..6ea39007c8a3 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -46,24 +46,6 @@ extern unsigned long sm501_modify_reg(struct device *dev, unsigned long set, unsigned long clear); -/* sm501_gpio_set - * - * set the state of the given GPIO line -*/ - -extern void sm501_gpio_set(struct device *dev, - unsigned long gpio, - unsigned int to, - unsigned int dir); - -/* sm501_gpio_get - * - * get the state of the given GPIO line -*/ - -extern unsigned long sm501_gpio_get(struct device *dev, - unsigned long gpio); - /* Platform data definitions */ @@ -131,6 +113,7 @@ struct sm501_reg_init { #define SM501_USE_FBACCEL (1<<6) #define SM501_USE_AC97 (1<<7) #define SM501_USE_I2S (1<<8) +#define SM501_USE_GPIO (1<<9) #define SM501_USE_ALL (0xffffffff) @@ -173,6 +156,7 @@ struct sm501_platdata { struct sm501_platdata_fb *fb; int flags; + unsigned gpio_base; int (*get_power)(struct device *dev); int (*set_power)(struct device *dev, unsigned int on); -- cgit From 60e540d617b40eb3d37f1dd99c97af588ff9b70b Mon Sep 17 00:00:00 2001 From: Arnaud Patard Date: Fri, 25 Jul 2008 01:46:00 -0700 Subject: sm501: gpio dynamic registration for PCI devices The SM501 PCI card requires a dyanmic gpio allocation as the number of cards is not known at compile time. Fixup the platform data and registration to deal with this. Acked-by: Ben Dooks Signed-off-by: Arnaud Patard Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mfd/sm501.c | 6 ++++-- include/linux/sm501.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index be8713908125..c3e5a48f6148 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -996,12 +996,13 @@ static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm, { struct sm501_platdata *pdata = sm->platdata; struct gpio_chip *gchip = &chip->gpio; - unsigned base = pdata->gpio_base; + int base = pdata->gpio_base; memcpy(chip, &gpio_chip_template, sizeof(struct gpio_chip)); if (chip == &gpio->high) { - base += 32; + if (base > 0) + base += 32; chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH; gchip->label = "SM501-HIGH"; } else { @@ -1452,6 +1453,7 @@ static struct sm501_platdata_fb sm501_fb_pdata = { static struct sm501_platdata sm501_pci_platdata = { .init = &sm501_pci_initdata, .fb = &sm501_fb_pdata, + .gpio_base = -1, }; static int sm501_pci_probe(struct pci_dev *dev, diff --git a/include/linux/sm501.h b/include/linux/sm501.h index 6ea39007c8a3..a8d02f36ad32 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -156,7 +156,7 @@ struct sm501_platdata { struct sm501_platdata_fb *fb; int flags; - unsigned gpio_base; + int gpio_base; int (*get_power)(struct device *dev); int (*set_power)(struct device *dev, unsigned int on); -- cgit From 42cd2366fb9b58cdfc1855be32b31a78e40b2079 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 25 Jul 2008 01:46:01 -0700 Subject: sm501: gpio I2C support Add support for adding the GPIO based I2C resources. Signed-off-by: Ben Dooks Cc: Arnaud Patard Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mfd/sm501.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/sm501.h | 10 ++++++- 2 files changed, 84 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index c3e5a48f6148..107215b28805 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -1086,6 +1087,11 @@ static void sm501_gpio_remove(struct sm501_devdata *sm) dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n"); } +static int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin) +{ + struct sm501_gpio *gpio = &sm->gpio; + return pin + (pin < 32) ? gpio->low.gpio.base : gpio->high.gpio.base; +} #else static int sm501_register_gpio(struct sm501_devdata *sm) { @@ -1095,8 +1101,66 @@ static int sm501_register_gpio(struct sm501_devdata *sm) static void sm501_gpio_remove(struct sm501_devdata *sm) { } + +static int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin) +{ + return -1; +} #endif +static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, + struct sm501_platdata_gpio_i2c *iic) +{ + struct i2c_gpio_platform_data *icd; + struct platform_device *pdev; + + pdev = sm501_create_subdev(sm, "i2c-gpio", 0, + sizeof(struct i2c_gpio_platform_data)); + if (!pdev) + return -ENOMEM; + + icd = pdev->dev.platform_data; + + /* We keep the pin_sda and pin_scl fields relative in case the + * same platform data is passed to >1 SM501. + */ + + icd->sda_pin = sm501_gpio_pin2nr(sm, iic->pin_sda); + icd->scl_pin = sm501_gpio_pin2nr(sm, iic->pin_scl); + icd->timeout = iic->timeout; + icd->udelay = iic->udelay; + + /* note, we can't use either of the pin numbers, as the i2c-gpio + * driver uses the platform.id field to generate the bus number + * to register with the i2c core; The i2c core doesn't have enough + * entries to deal with anything we currently use. + */ + + pdev->id = iic->bus_num; + + dev_info(sm->dev, "registering i2c-%d: sda=%d (%d), scl=%d (%d)\n", + iic->bus_num, + icd->sda_pin, iic->pin_sda, icd->scl_pin, iic->pin_scl); + + return sm501_register_device(sm, pdev); +} + +static int sm501_register_gpio_i2c(struct sm501_devdata *sm, + struct sm501_platdata *pdata) +{ + struct sm501_platdata_gpio_i2c *iic = pdata->gpio_i2c; + int index; + int ret; + + for (index = 0; index < pdata->gpio_i2c_nr; index++, iic++) { + ret = sm501_register_gpio_i2c_instance(sm, iic); + if (ret < 0) + return ret; + } + + return 0; +} + /* sm501_dbg_regs * * Debug attribute to attach to parent device to show core registers @@ -1204,6 +1268,7 @@ static unsigned int sm501_mem_local[] = { static int sm501_init_dev(struct sm501_devdata *sm) { struct sm501_initdata *idata; + struct sm501_platdata *pdata; resource_size_t mem_avail; unsigned long dramctrl; unsigned long devid; @@ -1242,7 +1307,9 @@ static int sm501_init_dev(struct sm501_devdata *sm) /* check to see if we have some device initialisation */ - idata = sm->platdata ? sm->platdata->init : NULL; + pdata = sm->platdata; + idata = pdata ? pdata->init : NULL; + if (idata) { sm501_init_regs(sm, idata); @@ -1254,6 +1321,13 @@ static int sm501_init_dev(struct sm501_devdata *sm) sm501_register_gpio(sm); } + if (pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) { + if (!sm->gpio.registered) + dev_err(sm->dev, "no gpio registered for i2c gpio.\n"); + else + sm501_register_gpio_i2c(sm, pdata); + } + ret = sm501_check_clocks(sm); if (ret) { dev_err(sm->dev, "M1X and M clocks sourced from different " diff --git a/include/linux/sm501.h b/include/linux/sm501.h index a8d02f36ad32..214f93209b8c 100644 --- a/include/linux/sm501.h +++ b/include/linux/sm501.h @@ -86,11 +86,19 @@ struct sm501_platdata_fb { struct sm501_platdata_fbsub *fb_pnl; }; -/* gpio i2c */ +/* gpio i2c + * + * Note, we have to pass in the bus number, as the number used will be + * passed to the i2c-gpio driver's platform_device.id, subsequently used + * to register the i2c bus. +*/ struct sm501_platdata_gpio_i2c { + unsigned int bus_num; unsigned int pin_sda; unsigned int pin_scl; + int udelay; + int timeout; }; /* sm501_initdata -- cgit From ef53d9c5e4da147ecaa43c44c5e5945eb83970a2 Mon Sep 17 00:00:00 2001 From: Srinivasa D S Date: Fri, 25 Jul 2008 01:46:04 -0700 Subject: kprobes: improve kretprobe scalability with hashed locking Currently list of kretprobe instances are stored in kretprobe object (as used_instances,free_instances) and in kretprobe hash table. We have one global kretprobe lock to serialise the access to these lists. This causes only one kretprobe handler to execute at a time. Hence affects system performance, particularly on SMP systems and when return probe is set on lot of functions (like on all systemcalls). Solution proposed here gives fine-grain locks that performs better on SMP system compared to present kretprobe implementation. Solution: 1) Instead of having one global lock to protect kretprobe instances present in kretprobe object and kretprobe hash table. We will have two locks, one lock for protecting kretprobe hash table and another lock for kretporbe object. 2) We hold lock present in kretprobe object while we modify kretprobe instance in kretprobe object and we hold per-hash-list lock while modifying kretprobe instances present in that hash list. To prevent deadlock, we never grab a per-hash-list lock while holding a kretprobe lock. 3) We can remove used_instances from struct kretprobe, as we can track used instances of kretprobe instances using kretprobe hash table. Time duration for kernel compilation ("make -j 8") on a 8-way ppc64 system with return probes set on all systemcalls looks like this. cacheline non-cacheline Un-patched kernel aligned patch aligned patch =============================================================================== real 9m46.784s 9m54.412s 10m2.450s user 40m5.715s 40m7.142s 40m4.273s sys 2m57.754s 2m58.583s 3m17.430s =========================================================== Time duration for kernel compilation ("make -j 8) on the same system, when kernel is not probed. ========================= real 9m26.389s user 40m8.775s sys 2m7.283s ========================= Signed-off-by: Srinivasa DS Signed-off-by: Jim Keniston Acked-by: Ananth N Mavinakayanahalli Cc: Anil S Keshavamurthy Cc: David S. Miller Cc: Masami Hiramatsu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/kernel/kprobes.c | 6 +- arch/ia64/kernel/kprobes.c | 6 +- arch/powerpc/kernel/kprobes.c | 6 +- arch/s390/kernel/kprobes.c | 6 +- arch/sparc64/kernel/kprobes.c | 11 ++-- arch/x86/kernel/kprobes.c | 6 +- include/linux/kprobes.h | 7 ++- kernel/kprobes.c | 127 +++++++++++++++++++++++++++++------------- 8 files changed, 108 insertions(+), 67 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c index 5ee39e10c8d1..d28513f14d05 100644 --- a/arch/arm/kernel/kprobes.c +++ b/arch/arm/kernel/kprobes.c @@ -296,8 +296,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given @@ -337,7 +336,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) } kretprobe_assert(ri, orig_ret_address, trampoline_address); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); @@ -347,7 +346,6 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) return (void *)orig_ret_address; } -/* Called with kretprobe_lock held. */ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index 233434f4f88f..f07688da947c 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c @@ -429,8 +429,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) ((struct fnptr *)kretprobe_trampoline)->ip; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given @@ -485,7 +484,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) kretprobe_assert(ri, orig_ret_address, trampoline_address); reset_current_kprobe(); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); preempt_enable_no_resched(); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { @@ -500,7 +499,6 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) return 1; } -/* Called with kretprobe_lock held */ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index 4ba2af125450..de79915452c8 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c @@ -144,7 +144,6 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, kcb->kprobe_saved_msr = regs->msr; } -/* Called with kretprobe_lock held */ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { @@ -312,8 +311,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given @@ -352,7 +350,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, regs->nip = orig_ret_address; reset_current_kprobe(); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); preempt_enable_no_resched(); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 288ad490a6dd..4f82e5b5f879 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -270,7 +270,6 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, __ctl_store(kcb->kprobe_saved_ctl, 9, 11); } -/* Called with kretprobe_lock held */ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { @@ -377,8 +376,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given @@ -417,7 +415,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, regs->psw.addr = orig_ret_address | PSW_ADDR_AMODE; reset_current_kprobe(); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); preempt_enable_no_resched(); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c index f43b5d755354..201a6e547e4a 100644 --- a/arch/sparc64/kernel/kprobes.c +++ b/arch/sparc64/kernel/kprobes.c @@ -478,9 +478,9 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) return 0; } -/* Called with kretprobe_lock held. The value stored in the return - * address register is actually 2 instructions before where the - * callee will return to. Sequences usually look something like this +/* The value stored in the return address register is actually 2 + * instructions before where the callee will return to. + * Sequences usually look something like this * * call some_function <--- return register points here * nop <--- call delay slot @@ -512,8 +512,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given @@ -553,7 +552,7 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) regs->tnpc = orig_ret_address + 4; reset_current_kprobe(); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); preempt_enable_no_resched(); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 43c019f85f0d..6c27679ec6aa 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c @@ -431,7 +431,6 @@ static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) regs->ip = (unsigned long)p->ainsn.insn; } -/* Called with kretprobe_lock held */ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { @@ -682,8 +681,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(current); + kretprobe_hash_lock(current, &head, &flags); /* fixup registers */ #ifdef CONFIG_X86_64 regs->cs = __KERNEL_CS; @@ -732,7 +730,7 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) kretprobe_assert(ri, orig_ret_address, trampoline_address); - spin_unlock_irqrestore(&kretprobe_lock, flags); + kretprobe_hash_unlock(current, &flags); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 04a3556bdea6..0be7795655fa 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -157,11 +157,10 @@ struct kretprobe { int nmissed; size_t data_size; struct hlist_head free_instances; - struct hlist_head used_instances; + spinlock_t lock; }; struct kretprobe_instance { - struct hlist_node uflist; /* either on free list or used list */ struct hlist_node hlist; struct kretprobe *rp; kprobe_opcode_t *ret_addr; @@ -201,7 +200,6 @@ static inline int init_test_probes(void) } #endif /* CONFIG_KPROBES_SANITY_TEST */ -extern spinlock_t kretprobe_lock; extern struct mutex kprobe_mutex; extern int arch_prepare_kprobe(struct kprobe *p); extern void arch_arm_kprobe(struct kprobe *p); @@ -214,6 +212,9 @@ extern void kprobes_inc_nmissed_count(struct kprobe *p); /* Get the kprobe at this addr (if any) - called with preemption disabled */ struct kprobe *get_kprobe(void *addr); +void kretprobe_hash_lock(struct task_struct *tsk, + struct hlist_head **head, unsigned long *flags); +void kretprobe_hash_unlock(struct task_struct *tsk, unsigned long *flags); struct hlist_head * kretprobe_inst_table_head(struct task_struct *tsk); /* kprobe_running() will just return the current_kprobe on this CPU */ diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 1485ca8d0e00..cb0b3bde3617 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -62,6 +62,7 @@ addr = ((kprobe_opcode_t *)(kallsyms_lookup_name(name))) #endif +static int kprobes_initialized; static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; @@ -69,8 +70,15 @@ static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; static bool kprobe_enabled; DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */ -DEFINE_SPINLOCK(kretprobe_lock); /* Protects kretprobe_inst_table */ static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL; +static struct { + spinlock_t lock ____cacheline_aligned; +} kretprobe_table_locks[KPROBE_TABLE_SIZE]; + +static spinlock_t *kretprobe_table_lock_ptr(unsigned long hash) +{ + return &(kretprobe_table_locks[hash].lock); +} /* * Normally, functions that we'd want to prohibit kprobes in, are marked @@ -368,26 +376,53 @@ void __kprobes kprobes_inc_nmissed_count(struct kprobe *p) return; } -/* Called with kretprobe_lock held */ void __kprobes recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head) { + struct kretprobe *rp = ri->rp; + /* remove rp inst off the rprobe_inst_table */ hlist_del(&ri->hlist); - if (ri->rp) { - /* remove rp inst off the used list */ - hlist_del(&ri->uflist); - /* put rp inst back onto the free list */ - INIT_HLIST_NODE(&ri->uflist); - hlist_add_head(&ri->uflist, &ri->rp->free_instances); + INIT_HLIST_NODE(&ri->hlist); + if (likely(rp)) { + spin_lock(&rp->lock); + hlist_add_head(&ri->hlist, &rp->free_instances); + spin_unlock(&rp->lock); } else /* Unregistering */ hlist_add_head(&ri->hlist, head); } -struct hlist_head __kprobes *kretprobe_inst_table_head(struct task_struct *tsk) +void kretprobe_hash_lock(struct task_struct *tsk, + struct hlist_head **head, unsigned long *flags) +{ + unsigned long hash = hash_ptr(tsk, KPROBE_HASH_BITS); + spinlock_t *hlist_lock; + + *head = &kretprobe_inst_table[hash]; + hlist_lock = kretprobe_table_lock_ptr(hash); + spin_lock_irqsave(hlist_lock, *flags); +} + +void kretprobe_table_lock(unsigned long hash, unsigned long *flags) { - return &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)]; + spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash); + spin_lock_irqsave(hlist_lock, *flags); +} + +void kretprobe_hash_unlock(struct task_struct *tsk, unsigned long *flags) +{ + unsigned long hash = hash_ptr(tsk, KPROBE_HASH_BITS); + spinlock_t *hlist_lock; + + hlist_lock = kretprobe_table_lock_ptr(hash); + spin_unlock_irqrestore(hlist_lock, *flags); +} + +void kretprobe_table_unlock(unsigned long hash, unsigned long *flags) +{ + spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash); + spin_unlock_irqrestore(hlist_lock, *flags); } /* @@ -401,17 +436,21 @@ void __kprobes kprobe_flush_task(struct task_struct *tk) struct kretprobe_instance *ri; struct hlist_head *head, empty_rp; struct hlist_node *node, *tmp; - unsigned long flags = 0; + unsigned long hash, flags = 0; - INIT_HLIST_HEAD(&empty_rp); - spin_lock_irqsave(&kretprobe_lock, flags); - head = kretprobe_inst_table_head(tk); + if (unlikely(!kprobes_initialized)) + /* Early boot. kretprobe_table_locks not yet initialized. */ + return; + + hash = hash_ptr(tk, KPROBE_HASH_BITS); + head = &kretprobe_inst_table[hash]; + kretprobe_table_lock(hash, &flags); hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { if (ri->task == tk) recycle_rp_inst(ri, &empty_rp); } - spin_unlock_irqrestore(&kretprobe_lock, flags); - + kretprobe_table_unlock(hash, &flags); + INIT_HLIST_HEAD(&empty_rp); hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); kfree(ri); @@ -423,24 +462,29 @@ static inline void free_rp_inst(struct kretprobe *rp) struct kretprobe_instance *ri; struct hlist_node *pos, *next; - hlist_for_each_entry_safe(ri, pos, next, &rp->free_instances, uflist) { - hlist_del(&ri->uflist); + hlist_for_each_entry_safe(ri, pos, next, &rp->free_instances, hlist) { + hlist_del(&ri->hlist); kfree(ri); } } static void __kprobes cleanup_rp_inst(struct kretprobe *rp) { - unsigned long flags; + unsigned long flags, hash; struct kretprobe_instance *ri; struct hlist_node *pos, *next; + struct hlist_head *head; + /* No race here */ - spin_lock_irqsave(&kretprobe_lock, flags); - hlist_for_each_entry_safe(ri, pos, next, &rp->used_instances, uflist) { - ri->rp = NULL; - hlist_del(&ri->uflist); + for (hash = 0; hash < KPROBE_TABLE_SIZE; hash++) { + kretprobe_table_lock(hash, &flags); + head = &kretprobe_inst_table[hash]; + hlist_for_each_entry_safe(ri, pos, next, head, hlist) { + if (ri->rp == rp) + ri->rp = NULL; + } + kretprobe_table_unlock(hash, &flags); } - spin_unlock_irqrestore(&kretprobe_lock, flags); free_rp_inst(rp); } @@ -831,32 +875,37 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs) { struct kretprobe *rp = container_of(p, struct kretprobe, kp); - unsigned long flags = 0; + unsigned long hash, flags = 0; + struct kretprobe_instance *ri; /*TODO: consider to only swap the RA after the last pre_handler fired */ - spin_lock_irqsave(&kretprobe_lock, flags); + hash = hash_ptr(current, KPROBE_HASH_BITS); + spin_lock_irqsave(&rp->lock, flags); if (!hlist_empty(&rp->free_instances)) { - struct kretprobe_instance *ri; - ri = hlist_entry(rp->free_instances.first, - struct kretprobe_instance, uflist); + struct kretprobe_instance, hlist); + hlist_del(&ri->hlist); + spin_unlock_irqrestore(&rp->lock, flags); + ri->rp = rp; ri->task = current; if (rp->entry_handler && rp->entry_handler(ri, regs)) { - spin_unlock_irqrestore(&kretprobe_lock, flags); + spin_unlock_irqrestore(&rp->lock, flags); return 0; } arch_prepare_kretprobe(ri, regs); /* XXX(hch): why is there no hlist_move_head? */ - hlist_del(&ri->uflist); - hlist_add_head(&ri->uflist, &ri->rp->used_instances); - hlist_add_head(&ri->hlist, kretprobe_inst_table_head(ri->task)); - } else + INIT_HLIST_NODE(&ri->hlist); + kretprobe_table_lock(hash, &flags); + hlist_add_head(&ri->hlist, &kretprobe_inst_table[hash]); + kretprobe_table_unlock(hash, &flags); + } else { rp->nmissed++; - spin_unlock_irqrestore(&kretprobe_lock, flags); + spin_unlock_irqrestore(&rp->lock, flags); + } return 0; } @@ -892,7 +941,7 @@ static int __kprobes __register_kretprobe(struct kretprobe *rp, rp->maxactive = NR_CPUS; #endif } - INIT_HLIST_HEAD(&rp->used_instances); + spin_lock_init(&rp->lock); INIT_HLIST_HEAD(&rp->free_instances); for (i = 0; i < rp->maxactive; i++) { inst = kmalloc(sizeof(struct kretprobe_instance) + @@ -901,8 +950,8 @@ static int __kprobes __register_kretprobe(struct kretprobe *rp, free_rp_inst(rp); return -ENOMEM; } - INIT_HLIST_NODE(&inst->uflist); - hlist_add_head(&inst->uflist, &rp->free_instances); + INIT_HLIST_NODE(&inst->hlist); + hlist_add_head(&inst->hlist, &rp->free_instances); } rp->nmissed = 0; @@ -1009,6 +1058,7 @@ static int __init init_kprobes(void) for (i = 0; i < KPROBE_TABLE_SIZE; i++) { INIT_HLIST_HEAD(&kprobe_table[i]); INIT_HLIST_HEAD(&kretprobe_inst_table[i]); + spin_lock_init(&(kretprobe_table_locks[i].lock)); } /* @@ -1050,6 +1100,7 @@ static int __init init_kprobes(void) err = arch_init_kprobes(); if (!err) err = register_die_notifier(&kprobe_exceptions_nb); + kprobes_initialized = (err == 0); if (!err) init_test_probes(); -- cgit From d8f388d8dc8d4f36539dd37c1fff62cc404ea0fc Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 25 Jul 2008 01:46:07 -0700 Subject: gpio: sysfs interface This adds a simple sysfs interface for GPIOs. /sys/class/gpio /export ... asks the kernel to export a GPIO to userspace /unexport ... to return a GPIO to the kernel /gpioN ... for each exported GPIO #N /value ... always readable, writes fail for input GPIOs /direction ... r/w as: in, out (default low); write high, low /gpiochipN ... for each gpiochip; #N is its first GPIO /base ... (r/o) same as N /label ... (r/o) descriptive, not necessarily unique /ngpio ... (r/o) number of GPIOs; numbered N .. N+(ngpio - 1) GPIOs claimed by kernel code may be exported by its owner using a new gpio_export() call, which should be most useful for driver debugging. Such exports may optionally be done without a "direction" attribute. Userspace may ask to take over a GPIO by writing to a sysfs control file, helping to cope with incomplete board support or other "one-off" requirements that don't merit full kernel support: echo 23 > /sys/class/gpio/export ... will gpio_request(23, "sysfs") and gpio_export(23); use /sys/class/gpio/gpio-23/direction to (re)configure it, when that GPIO can be used as both input and output. echo 23 > /sys/class/gpio/unexport ... will gpio_free(23), when it was exported as above The extra D-space footprint is a few hundred bytes, except for the sysfs resources associated with each exported GPIO. The additional I-space footprint is about two thirds of the current size of gpiolib (!). Since no /dev node creation is involved, no "udev" support is needed. Related changes: * This adds a device pointer to "struct gpio_chip". When GPIO providers initialize that, sysfs gpio class devices become children of that device instead of being "virtual" devices. * The (few) gpio_chip providers which have such a device node have been updated. * Some gpio_chip drivers also needed to update their module "owner" field ... for which missing kerneldoc was added. * Some gpio_chips don't support input GPIOs. Those GPIOs are now flagged appropriately when the chip is registered. Based on previous patches, and discussion both on and off LKML. A Documentation/ABI/testing/sysfs-gpio update is ready to submit once this merges to mainline. [akpm@linux-foundation.org: a few maintenance build fixes] Signed-off-by: David Brownell Cc: Guennadi Liakhovetski Cc: Greg KH Cc: Kay Sievers Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/gpio.txt | 123 +++++++++- arch/arm/plat-omap/gpio.c | 3 + arch/avr32/mach-at32ap/pio.c | 2 + drivers/gpio/Kconfig | 15 ++ drivers/gpio/gpiolib.c | 536 +++++++++++++++++++++++++++++++++++++++++-- drivers/gpio/mcp23s08.c | 1 + drivers/gpio/pca953x.c | 1 + drivers/gpio/pcf857x.c | 1 + drivers/i2c/chips/tps65010.c | 2 + drivers/mfd/htc-egpio.c | 2 + include/asm-generic/gpio.h | 33 ++- include/linux/gpio.h | 13 ++ 12 files changed, 712 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index c35ca9e40d4c..8b69811a9642 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -347,15 +347,12 @@ necessarily be nonportable. Dynamic definition of GPIOs is not currently standard; for example, as a side effect of configuring an add-on board with some GPIO expanders. -These calls are purely for kernel space, but a userspace API could be built -on top of them. - GPIO implementor's framework (OPTIONAL) ======================================= As noted earlier, there is an optional implementation framework making it easier for platforms to support different kinds of GPIO controller using -the same programming interface. +the same programming interface. This framework is called "gpiolib". As a debugging aid, if debugfs is available a /sys/kernel/debug/gpio file will be found there. That will list all the controllers registered through @@ -439,4 +436,120 @@ becomes available. That may mean the device should not be registered until calls for that GPIO can work. One way to address such dependencies is for such gpio_chip controllers to provide setup() and teardown() callbacks to board specific code; those board specific callbacks would register devices -once all the necessary resources are available. +once all the necessary resources are available, and remove them later when +the GPIO controller device becomes unavailable. + + +Sysfs Interface for Userspace (OPTIONAL) +======================================== +Platforms which use the "gpiolib" implementors framework may choose to +configure a sysfs user interface to GPIOs. This is different from the +debugfs interface, since it provides control over GPIO direction and +value instead of just showing a gpio state summary. Plus, it could be +present on production systems without debugging support. + +Given approprate hardware documentation for the system, userspace could +know for example that GPIO #23 controls the write protect line used to +protect boot loader segments in flash memory. System upgrade procedures +may need to temporarily remove that protection, first importing a GPIO, +then changing its output state, then updating the code before re-enabling +the write protection. In normal use, GPIO #23 would never be touched, +and the kernel would have no need to know about it. + +Again depending on appropriate hardware documentation, on some systems +userspace GPIO can be used to determine system configuration data that +standard kernels won't know about. And for some tasks, simple userspace +GPIO drivers could be all that the system really needs. + +Note that standard kernel drivers exist for common "LEDs and Buttons" +GPIO tasks: "leds-gpio" and "gpio_keys", respectively. Use those +instead of talking directly to the GPIOs; they integrate with kernel +frameworks better than your userspace code could. + + +Paths in Sysfs +-------------- +There are three kinds of entry in /sys/class/gpio: + + - Control interfaces used to get userspace control over GPIOs; + + - GPIOs themselves; and + + - GPIO controllers ("gpio_chip" instances). + +That's in addition to standard files including the "device" symlink. + +The control interfaces are write-only: + + /sys/class/gpio/ + + "export" ... Userspace may ask the kernel to export control of + a GPIO to userspace by writing its number to this file. + + Example: "echo 19 > export" will create a "gpio19" node + for GPIO #19, if that's not requested by kernel code. + + "unexport" ... Reverses the effect of exporting to userspace. + + Example: "echo 19 > unexport" will remove a "gpio19" + node exported using the "export" file. + +GPIO signals have paths like /sys/class/gpio/gpio42/ (for GPIO #42) +and have the following read/write attributes: + + /sys/class/gpio/gpioN/ + + "direction" ... reads as either "in" or "out". This value may + normally be written. Writing as "out" defaults to + initializing the value as low. To ensure glitch free + operation, values "low" and "high" may be written to + configure the GPIO as an output with that initial value. + + Note that this attribute *will not exist* if the kernel + doesn't support changing the direction of a GPIO, or + it was exported by kernel code that didn't explicitly + allow userspace to reconfigure this GPIO's direction. + + "value" ... reads as either 0 (low) or 1 (high). If the GPIO + is configured as an output, this value may be written; + any nonzero value is treated as high. + +GPIO controllers have paths like /sys/class/gpio/chipchip42/ (for the +controller implementing GPIOs starting at #42) and have the following +read-only attributes: + + /sys/class/gpio/gpiochipN/ + + "base" ... same as N, the first GPIO managed by this chip + + "label" ... provided for diagnostics (not always unique) + + "ngpio" ... how many GPIOs this manges (N to N + ngpio - 1) + +Board documentation should in most cases cover what GPIOs are used for +what purposes. However, those numbers are not always stable; GPIOs on +a daughtercard might be different depending on the base board being used, +or other cards in the stack. In such cases, you may need to use the +gpiochip nodes (possibly in conjunction with schematics) to determine +the correct GPIO number to use for a given signal. + + +Exporting from Kernel code +-------------------------- +Kernel code can explicitly manage exports of GPIOs which have already been +requested using gpio_request(): + + /* export the GPIO to userspace */ + int gpio_export(unsigned gpio, bool direction_may_change); + + /* reverse gpio_export() */ + void gpio_unexport(); + +After a kernel driver requests a GPIO, it may only be made available in +the sysfs interface by gpio_export(). The driver can control whether the +signal direction may change. This helps drivers prevent userspace code +from accidentally clobbering important system state. + +This explicit exporting can help with debugging (by making some kinds +of experiments easier), or can provide an always-there interface that's +suitable for documenting as part of a board support package. diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c index 1903a3491ee9..d8e9c2c3f0f6 100644 --- a/arch/arm/plat-omap/gpio.c +++ b/arch/arm/plat-omap/gpio.c @@ -1488,6 +1488,9 @@ static int __init _omap_gpio_init(void) bank->chip.set = gpio_set; if (bank_is_mpuio(bank)) { bank->chip.label = "mpuio"; +#ifdef CONFIG_ARCH_OMAP1 + bank->chip.dev = &omap_mpuio_device.dev; +#endif bank->chip.base = OMAP_MPUIO(0); } else { bank->chip.label = "gpio"; diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index 60da03ba7117..296294f8ed81 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c @@ -360,6 +360,8 @@ static int __init pio_probe(struct platform_device *pdev) pio->chip.label = pio->name; pio->chip.base = pdev->id * 32; pio->chip.ngpio = 32; + pio->chip.dev = &pdev->dev; + pio->chip.owner = THIS_MODULE; pio->chip.direction_input = direction_input; pio->chip.get = gpio_get; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index fced1909cbba..6ec0e35b98e3 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -23,6 +23,21 @@ config DEBUG_GPIO slower. The diagnostics help catch the type of setup errors that are most common when setting up new platforms or boards. +config GPIO_SYSFS + bool "/sys/class/gpio/... (sysfs interface)" + depends on SYSFS && EXPERIMENTAL + help + Say Y here to add a sysfs interface for GPIOs. + + This is mostly useful to work around omissions in a system's + kernel support. Those are common in custom and semicustom + hardware assembled using standard kernels with a minimum of + custom patches. In those cases, userspace code may import + a given GPIO from the kernel, if no kernel driver requested it. + + Kernel drivers may also request that a particular GPIO be + exported to userspace; this can be useful when debugging. + # put expanders in the right section, in alphabetical order comment "I2C GPIO expanders:" diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index beaf6b3a37dc..8d2940517c99 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2,8 +2,11 @@ #include #include #include - -#include +#include +#include +#include +#include +#include /* Optional implementation infrastructure for GPIO interfaces. @@ -44,6 +47,8 @@ struct gpio_desc { #define FLAG_REQUESTED 0 #define FLAG_IS_OUT 1 #define FLAG_RESERVED 2 +#define FLAG_EXPORT 3 /* protected by sysfs_lock */ +#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */ #ifdef CONFIG_DEBUG_FS const char *label; @@ -151,6 +156,482 @@ err: return ret; } +#ifdef CONFIG_GPIO_SYSFS + +/* lock protects against unexport_gpio() being called while + * sysfs files are active. + */ +static DEFINE_MUTEX(sysfs_lock); + +/* + * /sys/class/gpio/gpioN... only for GPIOs that are exported + * /direction + * * MAY BE OMITTED if kernel won't allow direction changes + * * is read/write as "in" or "out" + * * may also be written as "high" or "low", initializing + * output value as specified ("out" implies "low") + * /value + * * always readable, subject to hardware behavior + * * may be writable, as zero/nonzero + * + * REVISIT there will likely be an attribute for configuring async + * notifications, e.g. to specify polling interval or IRQ trigger type + * that would for example trigger a poll() on the "value". + */ + +static ssize_t gpio_direction_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else + status = sprintf(buf, "%s\n", + test_bit(FLAG_IS_OUT, &desc->flags) + ? "out" : "in"); + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_direction_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else if (sysfs_streq(buf, "high")) + status = gpio_direction_output(gpio, 1); + else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) + status = gpio_direction_output(gpio, 0); + else if (sysfs_streq(buf, "in")) + status = gpio_direction_input(gpio); + else + status = -EINVAL; + + mutex_unlock(&sysfs_lock); + return status ? : size; +} + +static const DEVICE_ATTR(direction, 0644, + gpio_direction_show, gpio_direction_store); + +static ssize_t gpio_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else + status = sprintf(buf, "%d\n", gpio_get_value_cansleep(gpio)); + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_value_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else if (!test_bit(FLAG_IS_OUT, &desc->flags)) + status = -EPERM; + else { + long value; + + status = strict_strtol(buf, 0, &value); + if (status == 0) { + gpio_set_value_cansleep(gpio, value != 0); + status = size; + } + } + + mutex_unlock(&sysfs_lock); + return status; +} + +static /*const*/ DEVICE_ATTR(value, 0644, + gpio_value_show, gpio_value_store); + +static const struct attribute *gpio_attrs[] = { + &dev_attr_direction.attr, + &dev_attr_value.attr, + NULL, +}; + +static const struct attribute_group gpio_attr_group = { + .attrs = (struct attribute **) gpio_attrs, +}; + +/* + * /sys/class/gpio/gpiochipN/ + * /base ... matching gpio_chip.base (N) + * /label ... matching gpio_chip.label + * /ngpio ... matching gpio_chip.ngpio + */ + +static ssize_t chip_base_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", chip->base); +} +static DEVICE_ATTR(base, 0444, chip_base_show, NULL); + +static ssize_t chip_label_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", chip->label ? : ""); +} +static DEVICE_ATTR(label, 0444, chip_label_show, NULL); + +static ssize_t chip_ngpio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", chip->ngpio); +} +static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL); + +static const struct attribute *gpiochip_attrs[] = { + &dev_attr_base.attr, + &dev_attr_label.attr, + &dev_attr_ngpio.attr, + NULL, +}; + +static const struct attribute_group gpiochip_attr_group = { + .attrs = (struct attribute **) gpiochip_attrs, +}; + +/* + * /sys/class/gpio/export ... write-only + * integer N ... number of GPIO to export (full access) + * /sys/class/gpio/unexport ... write-only + * integer N ... number of GPIO to unexport + */ +static ssize_t export_store(struct class *class, const char *buf, size_t len) +{ + long gpio; + int status; + + status = strict_strtol(buf, 0, &gpio); + if (status < 0) + goto done; + + /* No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + + status = gpio_request(gpio, "sysfs"); + if (status < 0) + goto done; + + status = gpio_export(gpio, true); + if (status < 0) + gpio_free(gpio); + else + set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags); + +done: + if (status) + pr_debug("%s: status %d\n", __func__, status); + return status ? : len; +} + +static ssize_t unexport_store(struct class *class, const char *buf, size_t len) +{ + long gpio; + int status; + + status = strict_strtol(buf, 0, &gpio); + if (status < 0) + goto done; + + status = -EINVAL; + + /* reject bogus commands (gpio_unexport ignores them) */ + if (!gpio_is_valid(gpio)) + goto done; + + /* No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) { + status = 0; + gpio_free(gpio); + } +done: + if (status) + pr_debug("%s: status %d\n", __func__, status); + return status ? : len; +} + +static struct class_attribute gpio_class_attrs[] = { + __ATTR(export, 0200, NULL, export_store), + __ATTR(unexport, 0200, NULL, unexport_store), + __ATTR_NULL, +}; + +static struct class gpio_class = { + .name = "gpio", + .owner = THIS_MODULE, + + .class_attrs = gpio_class_attrs, +}; + + +/** + * gpio_export - export a GPIO through sysfs + * @gpio: gpio to make available, already requested + * @direction_may_change: true if userspace may change gpio direction + * Context: arch_initcall or later + * + * When drivers want to make a GPIO accessible to userspace after they + * have requested it -- perhaps while debugging, or as part of their + * public interface -- they may use this routine. If the GPIO can + * change direction (some can't) and the caller allows it, userspace + * will see "direction" sysfs attribute which may be used to change + * the gpio's direction. A "value" attribute will always be provided. + * + * Returns zero on success, else an error. + */ +int gpio_export(unsigned gpio, bool direction_may_change) +{ + unsigned long flags; + struct gpio_desc *desc; + int status = -EINVAL; + + /* can't export until sysfs is available ... */ + if (!gpio_class.p) { + pr_debug("%s: called too early!\n", __func__); + return -ENOENT; + } + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + spin_lock_irqsave(&gpio_lock, flags); + desc = &gpio_desc[gpio]; + if (test_bit(FLAG_REQUESTED, &desc->flags) + && !test_bit(FLAG_EXPORT, &desc->flags)) { + status = 0; + if (!desc->chip->direction_input + || !desc->chip->direction_output) + direction_may_change = false; + } + spin_unlock_irqrestore(&gpio_lock, flags); + + if (status == 0) { + struct device *dev; + + dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), + desc, "gpio%d", gpio); + if (dev) { + if (direction_may_change) + status = sysfs_create_group(&dev->kobj, + &gpio_attr_group); + else + status = device_create_file(dev, + &dev_attr_value); + if (status != 0) + device_unregister(dev); + } else + status = -ENODEV; + if (status == 0) + set_bit(FLAG_EXPORT, &desc->flags); + } + + mutex_unlock(&sysfs_lock); + +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + + return status; +} +EXPORT_SYMBOL_GPL(gpio_export); + +static int match_export(struct device *dev, void *data) +{ + return dev_get_drvdata(dev) == data; +} + +/** + * gpio_unexport - reverse effect of gpio_export() + * @gpio: gpio to make unavailable + * + * This is implicit on gpio_free(). + */ +void gpio_unexport(unsigned gpio) +{ + struct gpio_desc *desc; + int status = -EINVAL; + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + desc = &gpio_desc[gpio]; + if (test_bit(FLAG_EXPORT, &desc->flags)) { + struct device *dev = NULL; + + dev = class_find_device(&gpio_class, NULL, desc, match_export); + if (dev) { + clear_bit(FLAG_EXPORT, &desc->flags); + put_device(dev); + device_unregister(dev); + status = 0; + } else + status = -ENODEV; + } + + mutex_unlock(&sysfs_lock); +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); +} +EXPORT_SYMBOL_GPL(gpio_unexport); + +static int gpiochip_export(struct gpio_chip *chip) +{ + int status; + struct device *dev; + + /* Many systems register gpio chips for SOC support very early, + * before driver model support is available. In those cases we + * export this later, in gpiolib_sysfs_init() ... here we just + * verify that _some_ field of gpio_class got initialized. + */ + if (!gpio_class.p) + return 0; + + /* use chip->base for the ID; it's already known to be unique */ + mutex_lock(&sysfs_lock); + dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip, + "gpiochip%d", chip->base); + if (dev) { + status = sysfs_create_group(&dev->kobj, + &gpiochip_attr_group); + } else + status = -ENODEV; + chip->exported = (status == 0); + mutex_unlock(&sysfs_lock); + + if (status) { + unsigned long flags; + unsigned gpio; + + spin_lock_irqsave(&gpio_lock, flags); + gpio = chip->base; + while (gpio_desc[gpio].chip == chip) + gpio_desc[gpio++].chip = NULL; + spin_unlock_irqrestore(&gpio_lock, flags); + + pr_debug("%s: chip %s status %d\n", __func__, + chip->label, status); + } + + return status; +} + +static void gpiochip_unexport(struct gpio_chip *chip) +{ + int status; + struct device *dev; + + mutex_lock(&sysfs_lock); + dev = class_find_device(&gpio_class, NULL, chip, match_export); + if (dev) { + put_device(dev); + device_unregister(dev); + chip->exported = 0; + status = 0; + } else + status = -ENODEV; + mutex_unlock(&sysfs_lock); + + if (status) + pr_debug("%s: chip %s status %d\n", __func__, + chip->label, status); +} + +static int __init gpiolib_sysfs_init(void) +{ + int status; + unsigned long flags; + unsigned gpio; + + status = class_register(&gpio_class); + if (status < 0) + return status; + + /* Scan and register the gpio_chips which registered very + * early (e.g. before the class_register above was called). + * + * We run before arch_initcall() so chip->dev nodes can have + * registered, and so arch_initcall() can always gpio_export(). + */ + spin_lock_irqsave(&gpio_lock, flags); + for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) { + struct gpio_chip *chip; + + chip = gpio_desc[gpio].chip; + if (!chip || chip->exported) + continue; + + spin_unlock_irqrestore(&gpio_lock, flags); + status = gpiochip_export(chip); + spin_lock_irqsave(&gpio_lock, flags); + } + spin_unlock_irqrestore(&gpio_lock, flags); + + + return status; +} +postcore_initcall(gpiolib_sysfs_init); + +#else +static inline int gpiochip_export(struct gpio_chip *chip) +{ + return 0; +} + +static inline void gpiochip_unexport(struct gpio_chip *chip) +{ +} + +#endif /* CONFIG_GPIO_SYSFS */ + /** * gpiochip_add() - register a gpio_chip * @chip: the chip to register, with chip->base initialized @@ -160,6 +641,11 @@ err: * because the chip->base is invalid or already associated with a * different chip. Otherwise it returns zero as a success code. * + * When gpiochip_add() is called very early during boot, so that GPIOs + * can be freely used, the chip->dev device must be registered before + * the gpio framework's arch_initcall(). Otherwise sysfs initialization + * for GPIOs will fail rudely. + * * If chip->base is negative, this requests dynamic assignment of * a range of valid GPIOs. */ @@ -182,7 +668,7 @@ int gpiochip_add(struct gpio_chip *chip) base = gpiochip_find_base(chip->ngpio); if (base < 0) { status = base; - goto fail_unlock; + goto unlock; } chip->base = base; } @@ -197,12 +683,23 @@ int gpiochip_add(struct gpio_chip *chip) if (status == 0) { for (id = base; id < base + chip->ngpio; id++) { gpio_desc[id].chip = chip; - gpio_desc[id].flags = 0; + + /* REVISIT: most hardware initializes GPIOs as + * inputs (often with pullups enabled) so power + * usage is minimized. Linux code should set the + * gpio direction first thing; but until it does, + * we may expose the wrong direction in sysfs. + */ + gpio_desc[id].flags = !chip->direction_input + ? (1 << FLAG_IS_OUT) + : 0; } } -fail_unlock: +unlock: spin_unlock_irqrestore(&gpio_lock, flags); + if (status == 0) + status = gpiochip_export(chip); fail: /* failures here can mean systems won't boot... */ if (status) @@ -239,6 +736,10 @@ int gpiochip_remove(struct gpio_chip *chip) } spin_unlock_irqrestore(&gpio_lock, flags); + + if (status == 0) + gpiochip_unexport(chip); + return status; } EXPORT_SYMBOL_GPL(gpiochip_remove); @@ -296,6 +797,8 @@ void gpio_free(unsigned gpio) return; } + gpio_unexport(gpio); + spin_lock_irqsave(&gpio_lock, flags); desc = &gpio_desc[gpio]; @@ -534,10 +1037,6 @@ EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); #ifdef CONFIG_DEBUG_FS -#include -#include - - static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) { unsigned i; @@ -614,17 +1113,28 @@ static int gpiolib_show(struct seq_file *s, void *unused) /* REVISIT this isn't locked against gpio_chip removal ... */ for (gpio = 0; gpio_is_valid(gpio); gpio++) { + struct device *dev; + if (chip == gpio_desc[gpio].chip) continue; chip = gpio_desc[gpio].chip; if (!chip) continue; - seq_printf(s, "%sGPIOs %d-%d, %s%s:\n", + seq_printf(s, "%sGPIOs %d-%d", started ? "\n" : "", - chip->base, chip->base + chip->ngpio - 1, - chip->label ? : "generic", - chip->can_sleep ? ", can sleep" : ""); + chip->base, chip->base + chip->ngpio - 1); + dev = chip->dev; + if (dev) + seq_printf(s, ", %s/%s", + dev->bus ? dev->bus->name : "no-bus", + dev->bus_id); + if (chip->label) + seq_printf(s, ", %s", chip->label); + if (chip->can_sleep) + seq_printf(s, ", can sleep"); + seq_printf(s, ":\n"); + started = 1; if (chip->dbg_show) chip->dbg_show(s, chip); diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c index 7f92fdd5f0e2..7efd7d3a81f9 100644 --- a/drivers/gpio/mcp23s08.c +++ b/drivers/gpio/mcp23s08.c @@ -239,6 +239,7 @@ static int mcp23s08_probe(struct spi_device *spi) mcp->chip.base = pdata->base; mcp->chip.ngpio = 8; mcp->chip.can_sleep = 1; + mcp->chip.dev = &spi->dev; mcp->chip.owner = THIS_MODULE; spi_set_drvdata(spi, mcp); diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index a380730b61ab..cc8468692ae0 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -188,6 +188,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->base = chip->gpio_start; gc->ngpio = gpios; gc->label = chip->client->name; + gc->dev = &chip->client->dev; gc->owner = THIS_MODULE; } diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c index d25d356c4f20..fc9c6ae739ee 100644 --- a/drivers/gpio/pcf857x.c +++ b/drivers/gpio/pcf857x.c @@ -200,6 +200,7 @@ static int pcf857x_probe(struct i2c_client *client, gpio->chip.base = pdata->gpio_base; gpio->chip.can_sleep = 1; + gpio->chip.dev = &client->dev; gpio->chip.owner = THIS_MODULE; /* NOTE: the OnSemi jlc1562b is also largely compatible with diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c index 85949685191b..cf02e8fceb42 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/i2c/chips/tps65010.c @@ -636,6 +636,8 @@ static int tps65010_probe(struct i2c_client *client, tps->outmask = board->outmask; tps->chip.label = client->name; + tps->chip.dev = &client->dev; + tps->chip.owner = THIS_MODULE; tps->chip.set = tps65010_gpio_set; tps->chip.direction_output = tps65010_output; diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c index 8872cc077519..6be43172dc65 100644 --- a/drivers/mfd/htc-egpio.c +++ b/drivers/mfd/htc-egpio.c @@ -318,6 +318,8 @@ static int __init egpio_probe(struct platform_device *pdev) ei->chip[i].dev = &(pdev->dev); chip = &(ei->chip[i].chip); chip->label = "htc-egpio"; + chip->dev = &pdev->dev; + chip->owner = THIS_MODULE; chip->get = egpio_get; chip->set = egpio_set; chip->direction_input = egpio_direction_input; diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 6be061d09da9..1beff5166e53 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -32,6 +32,8 @@ struct module; /** * struct gpio_chip - abstract a GPIO controller * @label: for diagnostics + * @dev: optional device providing the GPIOs + * @owner: helps prevent removal of modules exporting active GPIOs * @direction_input: configures signal "offset" as input, or returns error * @get: returns value for signal "offset"; for output signals this * returns either the value actually sensed, or zero @@ -59,6 +61,7 @@ struct module; */ struct gpio_chip { char *label; + struct device *dev; struct module *owner; int (*direction_input)(struct gpio_chip *chip, @@ -74,6 +77,7 @@ struct gpio_chip { int base; u16 ngpio; unsigned can_sleep:1; + unsigned exported:1; }; extern const char *gpiochip_is_requested(struct gpio_chip *chip, @@ -108,7 +112,18 @@ extern void __gpio_set_value(unsigned gpio, int value); extern int __gpio_cansleep(unsigned gpio); -#else +#ifdef CONFIG_GPIO_SYSFS + +/* + * A sysfs interface can be exported by individual drivers if they want, + * but more typically is configured entirely from userspace. + */ +extern int gpio_export(unsigned gpio, bool direction_may_change); +extern void gpio_unexport(unsigned gpio); + +#endif /* CONFIG_GPIO_SYSFS */ + +#else /* !CONFIG_HAVE_GPIO_LIB */ static inline int gpio_is_valid(int number) { @@ -137,6 +152,20 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value) gpio_set_value(gpio, value); } -#endif +#endif /* !CONFIG_HAVE_GPIO_LIB */ + +#ifndef CONFIG_GPIO_SYSFS + +/* sysfs support is only available with gpiolib, where it's optional */ + +static inline int gpio_export(unsigned gpio, bool direction_may_change) +{ + return -ENOSYS; +} + +static inline void gpio_unexport(unsigned gpio) +{ +} +#endif /* CONFIG_GPIO_SYSFS */ #endif /* _ASM_GENERIC_GPIO_H */ diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 98be6c5762b9..730a20b83576 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -79,6 +79,19 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value) WARN_ON(1); } +static inline int gpio_export(unsigned gpio, bool direction_may_change) +{ + /* GPIO can never have been requested or set as {in,out}put */ + WARN_ON(1); + return -EINVAL; +} + +static inline void gpio_unexport(unsigned gpio) +{ + /* GPIO can never have been exported */ + WARN_ON(1); +} + static inline int gpio_to_irq(unsigned gpio) { /* GPIO can never have been requested or set as input */ -- cgit From 8f1cc3b10e6ee0c5c7c8ed27f8771c4f252b4862 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 25 Jul 2008 01:46:09 -0700 Subject: gpio: mcp23s08 handles multiple chips per chipselect Teach the mcp23s08 driver about a curious feature of these chips: up to four of them can share the same chipselect, with the SPI signals wired in parallel, by matching two bits in the first protocol byte against two address lines on the chip. This is handled by three software changes: * Platform data now holds an array of per-chip structs, not just one chip's address and pullup configuration. * Probe() and remove() now use another level of structure, wrapping an instance of the original structure for each mcp23s08 chip sharing that chipselect. * The HAEN bit is set, so that the hardware address bits can no longer be ignored (boot firmware may not have enabled them). The "one struct per chip" preserves the guts of the current code, but platform_data will need minor changes. OLD: /* incorrect "slave" ID may not have mattered */ .slave = 3, .pullups = BIT(3) | BIT(1) | BIT(0), NEW: /* slave address _must_ match chip's wiring */ .chip[3] = { .is_present = true, .pullups = BIT(3) | BIT(1) | BIT(0), }, There's no change in how things _behave_ for spi_device nodes with a single mcp23s08 chip. New multi-chip configurations assign GPIOs in sequence, without holes. The spi_device just resembles a bigger controller, but internally it has multiple gpio_chip instances. Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/mcp23s08.c | 133 +++++++++++++++++++++++++++++++++---------- include/linux/spi/mcp23s08.h | 25 +++++--- 2 files changed, 118 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c index 7efd7d3a81f9..8a1b405fefda 100644 --- a/drivers/gpio/mcp23s08.c +++ b/drivers/gpio/mcp23s08.c @@ -40,15 +40,26 @@ struct mcp23s08 { struct spi_device *spi; u8 addr; + u8 cache[11]; /* lock protects the cached values */ struct mutex lock; - u8 cache[11]; struct gpio_chip chip; struct work_struct work; }; +/* A given spi_device can represent up to four mcp23s08 chips + * sharing the same chipselect but using different addresses + * (e.g. chips #0 and #3 might be populated, but not #1 or $2). + * Driver data holds all the per-chip data. + */ +struct mcp23s08_driver_data { + unsigned ngpio; + struct mcp23s08 *mcp[4]; + struct mcp23s08 chip[]; +}; + static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) { u8 tx[2], rx[1]; @@ -208,25 +219,18 @@ done: /*----------------------------------------------------------------------*/ -static int mcp23s08_probe(struct spi_device *spi) +static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, + unsigned base, unsigned pullups) { - struct mcp23s08 *mcp; - struct mcp23s08_platform_data *pdata; + struct mcp23s08_driver_data *data = spi_get_drvdata(spi); + struct mcp23s08 *mcp = data->mcp[addr]; int status; int do_update = 0; - pdata = spi->dev.platform_data; - if (!pdata || pdata->slave > 3 || !pdata->base) - return -ENODEV; - - mcp = kzalloc(sizeof *mcp, GFP_KERNEL); - if (!mcp) - return -ENOMEM; - mutex_init(&mcp->lock); mcp->spi = spi; - mcp->addr = 0x40 | (pdata->slave << 1); + mcp->addr = 0x40 | (addr << 1); mcp->chip.label = "mcp23s08", @@ -236,27 +240,28 @@ static int mcp23s08_probe(struct spi_device *spi) mcp->chip.set = mcp23s08_set; mcp->chip.dbg_show = mcp23s08_dbg_show; - mcp->chip.base = pdata->base; + mcp->chip.base = base; mcp->chip.ngpio = 8; mcp->chip.can_sleep = 1; mcp->chip.dev = &spi->dev; mcp->chip.owner = THIS_MODULE; - spi_set_drvdata(spi, mcp); - - /* verify MCP_IOCON.SEQOP = 0, so sequential reads work */ + /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, + * and MCP_IOCON.HAEN = 1, so we work with all chips. + */ status = mcp23s08_read(mcp, MCP_IOCON); if (status < 0) goto fail; - if (status & IOCON_SEQOP) { + if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { status &= ~IOCON_SEQOP; + status |= IOCON_HAEN; status = mcp23s08_write(mcp, MCP_IOCON, (u8) status); if (status < 0) goto fail; } /* configure ~100K pullups */ - status = mcp23s08_write(mcp, MCP_GPPU, pdata->pullups); + status = mcp23s08_write(mcp, MCP_GPPU, pullups); if (status < 0) goto fail; @@ -283,11 +288,58 @@ static int mcp23s08_probe(struct spi_device *spi) tx[1] = MCP_IPOL; memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2); status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); - - /* FIXME check status... */ + if (status < 0) + goto fail; } status = gpiochip_add(&mcp->chip); +fail: + if (status < 0) + dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n", + addr, status); + return status; +} + +static int mcp23s08_probe(struct spi_device *spi) +{ + struct mcp23s08_platform_data *pdata; + unsigned addr; + unsigned chips = 0; + struct mcp23s08_driver_data *data; + int status; + unsigned base; + + pdata = spi->dev.platform_data; + if (!pdata || !gpio_is_valid(pdata->base)) + return -ENODEV; + + for (addr = 0; addr < 4; addr++) { + if (!pdata->chip[addr].is_present) + continue; + chips++; + } + if (!chips) + return -ENODEV; + + data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08), + GFP_KERNEL); + if (!data) + return -ENOMEM; + spi_set_drvdata(spi, data); + + base = pdata->base; + for (addr = 0; addr < 4; addr++) { + if (!pdata->chip[addr].is_present) + continue; + chips--; + data->mcp[addr] = &data->chip[chips]; + status = mcp23s08_probe_one(spi, addr, base, + pdata->chip[addr].pullups); + if (status < 0) + goto fail; + base += 8; + } + data->ngpio = base - pdata->base; /* NOTE: these chips have a relatively sane IRQ framework, with * per-signal masking and level/edge triggering. It's not yet @@ -295,8 +347,9 @@ static int mcp23s08_probe(struct spi_device *spi) */ if (pdata->setup) { - status = pdata->setup(spi, mcp->chip.base, - mcp->chip.ngpio, pdata->context); + status = pdata->setup(spi, + pdata->base, data->ngpio, + pdata->context); if (status < 0) dev_dbg(&spi->dev, "setup --> %d\n", status); } @@ -304,19 +357,29 @@ static int mcp23s08_probe(struct spi_device *spi) return 0; fail: - kfree(mcp); + for (addr = 0; addr < 4; addr++) { + int tmp; + + if (!data->mcp[addr]) + continue; + tmp = gpiochip_remove(&data->mcp[addr]->chip); + if (tmp < 0) + dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); + } + kfree(data); return status; } static int mcp23s08_remove(struct spi_device *spi) { - struct mcp23s08 *mcp = spi_get_drvdata(spi); + struct mcp23s08_driver_data *data = spi_get_drvdata(spi); struct mcp23s08_platform_data *pdata = spi->dev.platform_data; + unsigned addr; int status = 0; if (pdata->teardown) { status = pdata->teardown(spi, - mcp->chip.base, mcp->chip.ngpio, + pdata->base, data->ngpio, pdata->context); if (status < 0) { dev_err(&spi->dev, "%s --> %d\n", "teardown", status); @@ -324,11 +387,20 @@ static int mcp23s08_remove(struct spi_device *spi) } } - status = gpiochip_remove(&mcp->chip); + for (addr = 0; addr < 4; addr++) { + int tmp; + + if (!data->mcp[addr]) + continue; + + tmp = gpiochip_remove(&data->mcp[addr]->chip); + if (tmp < 0) { + dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); + status = tmp; + } + } if (status == 0) - kfree(mcp); - else - dev_err(&spi->dev, "%s --> %d\n", "remove", status); + kfree(data); return status; } @@ -356,4 +428,3 @@ static void __exit mcp23s08_exit(void) module_exit(mcp23s08_exit); MODULE_LICENSE("GPL"); - diff --git a/include/linux/spi/mcp23s08.h b/include/linux/spi/mcp23s08.h index 835ddf47d45c..22ef107d7704 100644 --- a/include/linux/spi/mcp23s08.h +++ b/include/linux/spi/mcp23s08.h @@ -1,18 +1,25 @@ -/* FIXME driver should be able to handle all four slaves that - * can be hooked up to each chipselect, as well as IRQs... - */ +/* FIXME driver should be able to handle IRQs... */ + +struct mcp23s08_chip_info { + bool is_present; /* true iff populated */ + u8 pullups; /* BIT(x) means enable pullup x */ +}; struct mcp23s08_platform_data { - /* four slaves can share one SPI chipselect */ - u8 slave; + /* Four slaves (numbered 0..3) can share one SPI chipselect, and + * will provide 8..32 GPIOs using 1..4 gpio_chip instances. + */ + struct mcp23s08_chip_info chip[4]; - /* number assigned to the first GPIO */ + /* "base" is the number of the first GPIO. Dynamic assignment is + * not currently supported, and even if there are gaps in chip + * addressing the GPIO numbers are sequential .. so for example + * if only slaves 0 and 3 are present, their GPIOs range from + * base to base+15. + */ unsigned base; - /* pins with pullups */ - u8 pullups; - void *context; /* param to setup/teardown */ int (*setup)(struct spi_device *spi, -- cgit From bbcd6d543de335bf81e96477f46a60a8bf51039c Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 25 Jul 2008 01:46:14 -0700 Subject: gpio: max732x driver This adds a driver supporting a family of I2C port expanders from Maxim, which includes the MAX7319 and MAX7320-7327 chips. [dbrownell@users.sourceforge.net: minor fixes] Signed-off-by: Jack Ren Signed-off-by: Eric Miao Acked-by: Jean Delvare Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/Kconfig | 19 +++ drivers/gpio/Makefile | 1 + drivers/gpio/max732x.c | 385 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/max732x.h | 19 +++ 4 files changed, 424 insertions(+) create mode 100644 drivers/gpio/max732x.c create mode 100644 include/linux/i2c/max732x.h (limited to 'include/linux') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5a355f829167..dbd42d6c93a7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -67,6 +67,25 @@ config GPIO_SYSFS comment "I2C GPIO expanders:" +config GPIO_MAX732X + tristate "MAX7319, MAX7320-7327 I2C Port Expanders" + depends on I2C + help + Say yes here to support the MAX7319, MAX7320-7327 series of I2C + Port Expanders. Each IO port on these chips has a fixed role of + Input (designated by 'I'), Push-Pull Output ('O'), or Open-Drain + Input and Output (designed by 'P'). The combinations are listed + below: + + 8 bits: max7319 (8I), max7320 (8O), max7321 (8P), + max7322 (4I4O), max7323 (4P4O) + + 16 bits: max7324 (8I8O), max7325 (8P8O), + max7326 (4I12O), max7327 (4P12O) + + Board setup code must specify the model to use, and the start + number for these GPIOs. + config GPIO_PCA953X tristate "PCA953x, PCA955x, and MAX7310 I/O ports" depends on I2C diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 8c45948d1fe7..01b4bbde1956 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,6 +5,7 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIO_MAX7301) += max7301.o +obj-$(CONFIG_GPIO_MAX732X) += max732x.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o diff --git a/drivers/gpio/max732x.c b/drivers/gpio/max732x.c new file mode 100644 index 000000000000..b51c8135ca28 --- /dev/null +++ b/drivers/gpio/max732x.c @@ -0,0 +1,385 @@ +/* + * max732x.c - I2C Port Expander with 8/16 I/O + * + * Copyright (C) 2007 Marvell International Ltd. + * Copyright (C) 2008 Jack Ren + * Copyright (C) 2008 Eric Miao + * + * Derived from drivers/gpio/pca953x.c + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + + +/* + * Each port of MAX732x (including MAX7319) falls into one of the + * following three types: + * + * - Push Pull Output + * - Input + * - Open Drain I/O + * + * designated by 'O', 'I' and 'P' individually according to MAXIM's + * datasheets. + * + * There are two groups of I/O ports, each group usually includes + * up to 8 I/O ports, and is accessed by a specific I2C address: + * + * - Group A : by I2C address 0b'110xxxx + * - Group B : by I2C address 0b'101xxxx + * + * where 'xxxx' is decided by the connections of pin AD2/AD0. The + * address used also affects the initial state of output signals. + * + * Within each group of ports, there are five known combinations of + * I/O ports: 4I4O, 4P4O, 8I, 8P, 8O, see the definitions below for + * the detailed organization of these ports. + * + * GPIO numbers start from 'gpio_base + 0' to 'gpio_base + 8/16', + * and GPIOs from GROUP_A are numbered before those from GROUP_B + * (if there are two groups). + * + * NOTE: MAX7328/MAX7329 are drop-in replacements for PCF8574/a, so + * they are not supported by this driver. + */ + +#define PORT_NONE 0x0 /* '/' No Port */ +#define PORT_OUTPUT 0x1 /* 'O' Push-Pull, Output Only */ +#define PORT_INPUT 0x2 /* 'I' Input Only */ +#define PORT_OPENDRAIN 0x3 /* 'P' Open-Drain, I/O */ + +#define IO_4I4O 0x5AA5 /* O7 O6 I5 I4 I3 I2 O1 O0 */ +#define IO_4P4O 0x5FF5 /* O7 O6 P5 P4 P3 P2 O1 O0 */ +#define IO_8I 0xAAAA /* I7 I6 I5 I4 I3 I2 I1 I0 */ +#define IO_8P 0xFFFF /* P7 P6 P5 P4 P3 P2 P1 P0 */ +#define IO_8O 0x5555 /* O7 O6 O5 O4 O3 O2 O1 O0 */ + +#define GROUP_A(x) ((x) & 0xffff) /* I2C Addr: 0b'110xxxx */ +#define GROUP_B(x) ((x) << 16) /* I2C Addr: 0b'101xxxx */ + +static const struct i2c_device_id max732x_id[] = { + { "max7319", GROUP_A(IO_8I) }, + { "max7320", GROUP_B(IO_8O) }, + { "max7321", GROUP_A(IO_8P) }, + { "max7322", GROUP_A(IO_4I4O) }, + { "max7323", GROUP_A(IO_4P4O) }, + { "max7324", GROUP_A(IO_8I) | GROUP_B(IO_8O) }, + { "max7325", GROUP_A(IO_8P) | GROUP_B(IO_8O) }, + { "max7326", GROUP_A(IO_4I4O) | GROUP_B(IO_8O) }, + { "max7327", GROUP_A(IO_4P4O) | GROUP_B(IO_8O) }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max732x_id); + +struct max732x_chip { + struct gpio_chip gpio_chip; + + struct i2c_client *client; /* "main" client */ + struct i2c_client *client_dummy; + struct i2c_client *client_group_a; + struct i2c_client *client_group_b; + + unsigned int mask_group_a; + unsigned int dir_input; + unsigned int dir_output; + + struct mutex lock; + uint8_t reg_out[2]; +}; + +static int max732x_write(struct max732x_chip *chip, int group_a, uint8_t val) +{ + struct i2c_client *client; + int ret; + + client = group_a ? chip->client_group_a : chip->client_group_b; + ret = i2c_smbus_write_byte(client, val); + if (ret < 0) { + dev_err(&client->dev, "failed writing\n"); + return ret; + } + + return 0; +} + +static int max732x_read(struct max732x_chip *chip, int group_a, uint8_t *val) +{ + struct i2c_client *client; + int ret; + + client = group_a ? chip->client_group_a : chip->client_group_b; + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "failed reading\n"); + return ret; + } + + *val = (uint8_t)ret; + return 0; +} + +static inline int is_group_a(struct max732x_chip *chip, unsigned off) +{ + return (1u << off) & chip->mask_group_a; +} + +static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + uint8_t reg_val; + int ret; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + ret = max732x_read(chip, is_group_a(chip, off), ®_val); + if (ret < 0) + return 0; + + return reg_val & (1u << (off & 0x7)); +} + +static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct max732x_chip *chip; + uint8_t reg_out, mask = 1u << (off & 0x7); + int ret; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + mutex_lock(&chip->lock); + + reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0]; + reg_out = (val) ? reg_out | mask : reg_out & ~mask; + + ret = max732x_write(chip, is_group_a(chip, off), reg_out); + if (ret < 0) + goto out; + + /* update the shadow register then */ + if (off > 7) + chip->reg_out[1] = reg_out; + else + chip->reg_out[0] = reg_out; +out: + mutex_unlock(&chip->lock); +} + +static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + unsigned int mask = 1u << off; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + if ((mask & chip->dir_input) == 0) { + dev_dbg(&chip->client->dev, "%s port %d is output only\n", + chip->client->name, off); + return -EACCES; + } + + return 0; +} + +static int max732x_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + struct max732x_chip *chip; + unsigned int mask = 1u << off; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + if ((mask & chip->dir_output) == 0) { + dev_dbg(&chip->client->dev, "%s port %d is input only\n", + chip->client->name, off); + return -EACCES; + } + + max732x_gpio_set_value(gc, off, val); + return 0; +} + +static int __devinit max732x_setup_gpio(struct max732x_chip *chip, + const struct i2c_device_id *id, + unsigned gpio_start) +{ + struct gpio_chip *gc = &chip->gpio_chip; + uint32_t id_data = id->driver_data; + int i, port = 0; + + for (i = 0; i < 16; i++, id_data >>= 2) { + unsigned int mask = 1 << port; + + switch (id_data & 0x3) { + case PORT_OUTPUT: + chip->dir_output |= mask; + break; + case PORT_INPUT: + chip->dir_input |= mask; + break; + case PORT_OPENDRAIN: + chip->dir_output |= mask; + chip->dir_input |= mask; + break; + default: + continue; + } + + if (i < 8) + chip->mask_group_a |= mask; + port++; + } + + if (chip->dir_input) + gc->direction_input = max732x_gpio_direction_input; + if (chip->dir_output) { + gc->direction_output = max732x_gpio_direction_output; + gc->set = max732x_gpio_set_value; + } + gc->get = max732x_gpio_get_value; + gc->can_sleep = 1; + + gc->base = gpio_start; + gc->ngpio = port; + gc->label = chip->client->name; + gc->owner = THIS_MODULE; + + return port; +} + +static int __devinit max732x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max732x_platform_data *pdata; + struct max732x_chip *chip; + struct i2c_client *c; + uint16_t addr_a, addr_b; + int ret, nr_port; + + pdata = client->dev.platform_data; + if (pdata == NULL) + return -ENODEV; + + chip = kzalloc(sizeof(struct max732x_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->client = client; + + nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base); + + addr_a = (client->addr & 0x0f) | 0x60; + addr_b = (client->addr & 0x0f) | 0x50; + + switch (client->addr & 0x70) { + case 0x60: + chip->client_group_a = client; + if (nr_port > 7) { + c = i2c_new_dummy(client->adapter, addr_b); + chip->client_group_b = chip->client_dummy = c; + } + break; + case 0x50: + chip->client_group_b = client; + if (nr_port > 7) { + c = i2c_new_dummy(client->adapter, addr_a); + chip->client_group_a = chip->client_dummy = c; + } + break; + default: + dev_err(&client->dev, "invalid I2C address specified %02x\n", + client->addr); + ret = -EINVAL; + goto out_failed; + } + + mutex_init(&chip->lock); + + max732x_read(chip, is_group_a(chip, 0), &chip->reg_out[0]); + if (nr_port > 7) + max732x_read(chip, is_group_a(chip, 8), &chip->reg_out[1]); + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto out_failed; + + if (pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + i2c_set_clientdata(client, chip); + return 0; + +out_failed: + kfree(chip); + return ret; +} + +static int __devexit max732x_remove(struct i2c_client *client) +{ + struct max732x_platform_data *pdata = client->dev.platform_data; + struct max732x_chip *chip = i2c_get_clientdata(client); + int ret; + + if (pdata->teardown) { + ret = pdata->teardown(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) { + dev_err(&client->dev, "%s failed, %d\n", + "teardown", ret); + return ret; + } + } + + ret = gpiochip_remove(&chip->gpio_chip); + if (ret) { + dev_err(&client->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + /* unregister any dummy i2c_client */ + if (chip->client_dummy) + i2c_unregister_device(chip->client_dummy); + + kfree(chip); + return 0; +} + +static struct i2c_driver max732x_driver = { + .driver = { + .name = "max732x", + .owner = THIS_MODULE, + }, + .probe = max732x_probe, + .remove = __devexit_p(max732x_remove), + .id_table = max732x_id, +}; + +static int __init max732x_init(void) +{ + return i2c_add_driver(&max732x_driver); +} +module_init(max732x_init); + +static void __exit max732x_exit(void) +{ + i2c_del_driver(&max732x_driver); +} +module_exit(max732x_exit); + +MODULE_AUTHOR("Eric Miao "); +MODULE_DESCRIPTION("GPIO expander driver for MAX732X"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/max732x.h b/include/linux/i2c/max732x.h new file mode 100644 index 000000000000..e10336631c62 --- /dev/null +++ b/include/linux/i2c/max732x.h @@ -0,0 +1,19 @@ +#ifndef __LINUX_I2C_MAX732X_H +#define __LINUX_I2C_MAX732X_H + +/* platform data for the MAX732x 8/16-bit I/O expander driver */ + +struct max732x_platform_data { + /* number of the first GPIO */ + unsigned gpio_base; + + void *context; /* param to setup/teardown */ + + int (*setup)(struct i2c_client *client, + unsigned gpio, unsigned ngpio, + void *context); + int (*teardown)(struct i2c_client *client, + unsigned gpio, unsigned ngpio, + void *context); +}; +#endif /* __LINUX_I2C_MAX732X_H */ -- cgit From 50c33a84db4aa5082e3af8d873b22344ae2ebea8 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Fri, 25 Jul 2008 01:46:16 -0700 Subject: ext2: fix typo in Hurd part of include/linux/ext2_fs.h Fix typo in Hurd part of include/linux/ext2_fs.h The ';' here is redundant or can even pose problem. This is actually not used by the Linux kernel, but it is exposed in GNU/Hurd. Signed-off-by: Samuel Thibault Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ext2_fs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 84cec2aa9f1e..2efe7b863cff 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -284,8 +284,8 @@ struct ext2_inode { #ifdef __hurd__ #define i_translator osd1.hurd1.h_i_translator -#define i_frag osd2.hurd2.h_i_frag; -#define i_fsize osd2.hurd2.h_i_fsize; +#define i_frag osd2.hurd2.h_i_frag +#define i_fsize osd2.hurd2.h_i_fsize #define i_uid_high osd2.hurd2.h_i_uid_high #define i_gid_high osd2.hurd2.h_i_gid_high #define i_author osd2.hurd2.h_i_author -- cgit From ae76dd9a6b5bbe5315fb7028e03f68f75b8538f3 Mon Sep 17 00:00:00 2001 From: Duane Griffin Date: Fri, 25 Jul 2008 01:46:23 -0700 Subject: ext3: handle corrupted orphan list at mount If the orphan node list includes valid, untruncatable nodes with nlink > 0 the ext3_orphan_cleanup loop which attempts to delete them will not do so, causing it to loop forever. Fix by checking for such nodes in the ext3_orphan_get function. This patch fixes the second case (image hdb.20000009.softlockup.gz) reported in http://bugzilla.kernel.org/show_bug.cgi?id=10882. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: printk warning fix] Signed-off-by: Duane Griffin Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext3/ialloc.c | 9 +++++++++ fs/ext3/inode.c | 20 ++++++++++++++------ include/linux/ext3_fs.h | 1 + 3 files changed, 24 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index 77126821b2e9..47b678d73e7a 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c @@ -669,6 +669,14 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) if (IS_ERR(inode)) goto iget_failed; + /* + * If the orphans has i_nlinks > 0 then it should be able to be + * truncated, otherwise it won't be removed from the orphan list + * during processing and an infinite loop will result. + */ + if (inode->i_nlink && !ext3_can_truncate(inode)) + goto bad_orphan; + if (NEXT_ORPHAN(inode) > max_ino) goto bad_orphan; brelse(bitmap_bh); @@ -690,6 +698,7 @@ bad_orphan: printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", NEXT_ORPHAN(inode)); printk(KERN_NOTICE "max_ino=%lu\n", max_ino); + printk(KERN_NOTICE "i_nlink=%u\n", inode->i_nlink); /* Avoid freeing blocks if we got a bad deleted inode */ if (inode->i_nlink == 0) inode->i_blocks = 0; diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 6ae4ecf3ce40..74b432fa166b 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -2253,6 +2253,19 @@ static void ext3_free_branches(handle_t *handle, struct inode *inode, } } +int ext3_can_truncate(struct inode *inode) +{ + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return 0; + if (S_ISREG(inode->i_mode)) + return 1; + if (S_ISDIR(inode->i_mode)) + return 1; + if (S_ISLNK(inode->i_mode)) + return !ext3_inode_is_fast_symlink(inode); + return 0; +} + /* * ext3_truncate() * @@ -2297,12 +2310,7 @@ void ext3_truncate(struct inode *inode) unsigned blocksize = inode->i_sb->s_blocksize; struct page *page; - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return; - if (ext3_inode_is_fast_symlink(inode)) - return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + if (!ext3_can_truncate(inode)) return; /* diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 36c540396377..80171ee89a22 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -832,6 +832,7 @@ extern void ext3_discard_reservation (struct inode *); extern void ext3_dirty_inode(struct inode *); extern int ext3_change_inode_journal_flag(struct inode *, int); extern int ext3_get_inode_loc(struct inode *, struct ext3_iloc *); +extern int ext3_can_truncate(struct inode *inode); extern void ext3_truncate (struct inode *); extern void ext3_set_inode_flags(struct inode *); extern void ext3_get_inode_flags(struct ext3_inode_info *); -- cgit From de0ca06a99c33df8333955642843331ab6b6e7ff Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:46:34 -0700 Subject: coda: remove CODA_FS_OLD_API While fixing CONFIG_ leakages to the userspace kernel headers I ran into CODA_FS_OLD_API. After five years, are there still people using the old API left? Especially considering that you have to choose at compile time which API to support in the kernel (and distributions tend to offer the new API for some time). Jan: "The old API can definitely go. Around the time the new interface went in there were some non-Coda userspace file system implementations that took a while longer to convert to the new API, but by now they all switched to the new interface or in some cases to a FUSE-based solution." Signed-off-by: Adrian Bunk Acked-by: Jan Harkes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/Kconfig | 14 -------------- fs/coda/coda_linux.c | 6 ++---- fs/coda/psdev.c | 4 ---- fs/coda/upcall.c | 15 +-------------- include/linux/coda.h | 43 ------------------------------------------- 5 files changed, 3 insertions(+), 79 deletions(-) (limited to 'include/linux') diff --git a/fs/Kconfig b/fs/Kconfig index 37db79a2ff95..ed563b9e352a 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -2093,20 +2093,6 @@ config CODA_FS To compile the coda client support as a module, choose M here: the module will be called coda. -config CODA_FS_OLD_API - bool "Use 96-bit Coda file identifiers" - depends on CODA_FS - help - A new kernel-userspace API had to be introduced for Coda v6.0 - to support larger 128-bit file identifiers as needed by the - new realms implementation. - - However this new API is not backward compatible with older - clients. If you really need to run the old Coda userspace - cache manager then say Y. - - For most cases you probably want to say N. - config AFS_FS tristate "Andrew File System support (AFS) (EXPERIMENTAL)" depends on INET && EXPERIMENTAL diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index e1c854890f94..bf4a3fd3c8e3 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -28,11 +28,9 @@ int coda_fake_statfs; char * coda_f2s(struct CodaFid *f) { static char s[60]; -#ifdef CONFIG_CODA_FS_OLD_API - sprintf(s, "(%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2]); -#else + sprintf(s, "(%08x.%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2], f->opaque[3]); -#endif + return s; } diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 40c36f7352a6..0d9b80ec689c 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -378,11 +378,7 @@ MODULE_AUTHOR("Jan Harkes, Peter J. Braam"); MODULE_DESCRIPTION("Coda Distributed File System VFS interface"); MODULE_ALIAS_CHARDEV_MAJOR(CODA_PSDEV_MAJOR); MODULE_LICENSE("GPL"); -#ifdef CONFIG_CODA_FS_OLD_API -MODULE_VERSION("5.3.21"); -#else MODULE_VERSION("6.6"); -#endif static int __init init_coda(void) { diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 359e531094dd..ce432bca95d1 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -52,12 +52,8 @@ static void *alloc_upcall(int opcode, int size) inp->ih.opcode = opcode; inp->ih.pid = current->pid; inp->ih.pgid = task_pgrp_nr(current); -#ifdef CONFIG_CODA_FS_OLD_API - memset(&inp->ih.cred, 0, sizeof(struct coda_cred)); - inp->ih.cred.cr_fsuid = current->fsuid; -#else inp->ih.uid = current->fsuid; -#endif + return (void*)inp; } @@ -166,20 +162,11 @@ int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, union inputArgs *inp; union outputArgs *outp; int insize, outsize, error; -#ifdef CONFIG_CODA_FS_OLD_API - struct coda_cred cred = { 0, }; - cred.cr_fsuid = uid; -#endif insize = SIZE(release); UPARG(CODA_CLOSE); -#ifdef CONFIG_CODA_FS_OLD_API - memcpy(&(inp->ih.cred), &cred, sizeof(cred)); -#else inp->ih.uid = uid; -#endif - inp->coda_close.VFid = *fid; inp->coda_close.flags = flags; diff --git a/include/linux/coda.h b/include/linux/coda.h index b5cf0780c51a..96c87693800b 100644 --- a/include/linux/coda.h +++ b/include/linux/coda.h @@ -199,28 +199,6 @@ typedef u_int32_t vuid_t; typedef u_int32_t vgid_t; #endif /*_VUID_T_ */ -#ifdef CONFIG_CODA_FS_OLD_API -struct CodaFid { - u_int32_t opaque[3]; -}; - -static __inline__ ino_t coda_f2i(struct CodaFid *fid) -{ - if ( ! fid ) - return 0; - if (fid->opaque[1] == 0xfffffffe || fid->opaque[1] == 0xffffffff) - return ((fid->opaque[0] << 20) | (fid->opaque[2] & 0xfffff)); - else - return (fid->opaque[2] + (fid->opaque[1]<<10) + (fid->opaque[0]<<20)); -} - -struct coda_cred { - vuid_t cr_uid, cr_euid, cr_suid, cr_fsuid; /* Real, efftve, set, fs uid*/ - vgid_t cr_groupid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */ -}; - -#else /* not defined(CONFIG_CODA_FS_OLD_API) */ - struct CodaFid { u_int32_t opaque[4]; }; @@ -228,8 +206,6 @@ struct CodaFid { #define coda_f2i(fid)\ (fid ? (fid->opaque[3] ^ (fid->opaque[2]<<10) ^ (fid->opaque[1]<<20) ^ fid->opaque[0]) : 0) -#endif - #ifndef _VENUS_VATTR_T_ #define _VENUS_VATTR_T_ /* @@ -313,15 +289,7 @@ struct coda_statfs { #define CIOC_KERNEL_VERSION _IOWR('c', 10, size_t) -#if 0 -#define CODA_KERNEL_VERSION 0 /* don't care about kernel version number */ -#define CODA_KERNEL_VERSION 1 /* The old venus 4.6 compatible interface */ -#endif -#ifdef CONFIG_CODA_FS_OLD_API -#define CODA_KERNEL_VERSION 2 /* venus_lookup got an extra parameter */ -#else #define CODA_KERNEL_VERSION 3 /* 128-bit file identifiers */ -#endif /* * Venus <-> Coda RPC arguments @@ -329,16 +297,9 @@ struct coda_statfs { struct coda_in_hdr { u_int32_t opcode; u_int32_t unique; /* Keep multiple outstanding msgs distinct */ -#ifdef CONFIG_CODA_FS_OLD_API - u_int16_t pid; /* Common to all */ - u_int16_t pgid; /* Common to all */ - u_int16_t sid; /* Common to all */ - struct coda_cred cred; /* Common to all */ -#else pid_t pid; pid_t pgid; vuid_t uid; -#endif }; /* Really important that opcode and unique are 1st two fields! */ @@ -613,11 +574,7 @@ struct coda_vget_out { /* CODA_PURGEUSER is a venus->kernel call */ struct coda_purgeuser_out { struct coda_out_hdr oh; -#ifdef CONFIG_CODA_FS_OLD_API - struct coda_cred cred; -#else vuid_t uid; -#endif }; /* coda_zapfile: */ -- cgit From f68215c4640a38d66429014e524a627bf572d26a Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 25 Jul 2008 01:46:38 -0700 Subject: reiserfs: convert j_lock to mutex j_lock is a semaphore but uses it as if it were a mutex. This patch converts it to a mutex. Signed-off-by: Jeff Mahoney Cc: Matthew Wilcox Cc: Chris Mason Cc: Edward Shishkin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/journal.c | 6 +++--- include/linux/reiserfs_fs_sb.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index e396b2fa4743..0f7b1e807e60 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -558,13 +558,13 @@ static inline void insert_journal_hash(struct reiserfs_journal_cnode **table, static inline void lock_journal(struct super_block *p_s_sb) { PROC_INFO_INC(p_s_sb, journal.lock_journal); - down(&SB_JOURNAL(p_s_sb)->j_lock); + mutex_lock(&SB_JOURNAL(p_s_sb)->j_mutex); } /* unlock the current transaction */ static inline void unlock_journal(struct super_block *p_s_sb) { - up(&SB_JOURNAL(p_s_sb)->j_lock); + mutex_unlock(&SB_JOURNAL(p_s_sb)->j_mutex); } static inline void get_journal_list(struct reiserfs_journal_list *jl) @@ -2837,7 +2837,7 @@ int journal_init(struct super_block *p_s_sb, const char *j_dev_name, journal->j_last = NULL; journal->j_first = NULL; init_waitqueue_head(&(journal->j_join_wait)); - sema_init(&journal->j_lock, 1); + mutex_init(&journal->j_mutex); sema_init(&journal->j_flush_sem, 1); journal->j_trans_id = 10; diff --git a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h index 336ee43ed7d8..49b639b88bac 100644 --- a/include/linux/reiserfs_fs_sb.h +++ b/include/linux/reiserfs_fs_sb.h @@ -193,7 +193,7 @@ struct reiserfs_journal { struct buffer_head *j_header_bh; time_t j_trans_start_time; /* time this transaction started */ - struct semaphore j_lock; + struct mutex j_mutex; struct semaphore j_flush_sem; wait_queue_head_t j_join_wait; /* wait for current transaction to finish before starting new one */ atomic_t j_jlock; /* lock for j_join_wait */ -- cgit From afe70259076fff0446001eaa1a287f615241a357 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 25 Jul 2008 01:46:39 -0700 Subject: reiserfs: convert j_flush_sem to mutex j_flush_sem is a semaphore but uses it as if it were a mutex. This patch converts it to a mutex. [akpm@linux-foundation.org: fix mutex_trylock retval treatment] Signed-off-by: Jeff Mahoney Cc: Matthew Wilcox Cc: Chris Mason Cc: Edward Shishkin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/journal.c | 14 +++++++------- include/linux/reiserfs_fs_sb.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index 0f7b1e807e60..3cb4a562030e 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -1411,8 +1411,8 @@ static int flush_journal_list(struct super_block *s, /* if flushall == 0, the lock is already held */ if (flushall) { - down(&journal->j_flush_sem); - } else if (!down_trylock(&journal->j_flush_sem)) { + mutex_lock(&journal->j_flush_mutex); + } else if (mutex_trylock(&journal->j_flush_mutex)) { BUG(); } @@ -1642,7 +1642,7 @@ static int flush_journal_list(struct super_block *s, jl->j_state = 0; put_journal_list(s, jl); if (flushall) - up(&journal->j_flush_sem); + mutex_unlock(&journal->j_flush_mutex); put_fs_excl(); return err; } @@ -1772,12 +1772,12 @@ static int kupdate_transactions(struct super_block *s, struct reiserfs_journal *journal = SB_JOURNAL(s); chunk.nr = 0; - down(&journal->j_flush_sem); + mutex_lock(&journal->j_flush_mutex); if (!journal_list_still_alive(s, orig_trans_id)) { goto done; } - /* we've got j_flush_sem held, nobody is going to delete any + /* we've got j_flush_mutex held, nobody is going to delete any * of these lists out from underneath us */ while ((num_trans && transactions_flushed < num_trans) || @@ -1812,7 +1812,7 @@ static int kupdate_transactions(struct super_block *s, } done: - up(&journal->j_flush_sem); + mutex_unlock(&journal->j_flush_mutex); return ret; } @@ -2838,7 +2838,7 @@ int journal_init(struct super_block *p_s_sb, const char *j_dev_name, journal->j_first = NULL; init_waitqueue_head(&(journal->j_join_wait)); mutex_init(&journal->j_mutex); - sema_init(&journal->j_flush_sem, 1); + mutex_init(&journal->j_flush_mutex); journal->j_trans_id = 10; journal->j_mount_id = 10; diff --git a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h index 49b639b88bac..c0751724ee64 100644 --- a/include/linux/reiserfs_fs_sb.h +++ b/include/linux/reiserfs_fs_sb.h @@ -194,7 +194,7 @@ struct reiserfs_journal { time_t j_trans_start_time; /* time this transaction started */ struct mutex j_mutex; - struct semaphore j_flush_sem; + struct mutex j_flush_mutex; wait_queue_head_t j_join_wait; /* wait for current transaction to finish before starting new one */ atomic_t j_jlock; /* lock for j_join_wait */ int j_list_bitmap_index; /* number of next list bitmap to use */ -- cgit From 90415deac75a761a25239af6f56381546f8d2201 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 25 Jul 2008 01:46:40 -0700 Subject: reiserfs: convert j_commit_lock to mutex j_commit_lock is a semaphore but uses it as if it were a mutex. This patch converts it to a mutex. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Jeff Mahoney Cc: Matthew Wilcox Cc: Chris Mason Cc: Edward Shishkin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/journal.c | 22 ++++++++++------------ include/linux/reiserfs_fs_sb.h | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index 3cb4a562030e..c8f60ee183b5 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -34,15 +34,10 @@ ** from within kupdate, it will ignore the immediate flag */ -#include -#include - #include #include - #include #include - #include #include #include @@ -54,6 +49,9 @@ #include #include #include +#include + +#include /* gets a struct reiserfs_journal_list * from a list head */ #define JOURNAL_LIST_ENTRY(h) (list_entry((h), struct reiserfs_journal_list, \ @@ -1045,9 +1043,9 @@ static int flush_commit_list(struct super_block *s, } /* make sure nobody is trying to flush this one at the same time */ - down(&jl->j_commit_lock); + mutex_lock(&jl->j_commit_mutex); if (!journal_list_still_alive(s, trans_id)) { - up(&jl->j_commit_lock); + mutex_unlock(&jl->j_commit_mutex); goto put_jl; } BUG_ON(jl->j_trans_id == 0); @@ -1057,7 +1055,7 @@ static int flush_commit_list(struct super_block *s, if (flushall) { atomic_set(&(jl->j_older_commits_done), 1); } - up(&jl->j_commit_lock); + mutex_unlock(&jl->j_commit_mutex); goto put_jl; } @@ -1181,7 +1179,7 @@ static int flush_commit_list(struct super_block *s, if (flushall) { atomic_set(&(jl->j_older_commits_done), 1); } - up(&jl->j_commit_lock); + mutex_unlock(&jl->j_commit_mutex); put_jl: put_journal_list(s, jl); @@ -2556,7 +2554,7 @@ static struct reiserfs_journal_list *alloc_journal_list(struct super_block *s) INIT_LIST_HEAD(&jl->j_working_list); INIT_LIST_HEAD(&jl->j_tail_bh_list); INIT_LIST_HEAD(&jl->j_bh_list); - sema_init(&jl->j_commit_lock, 1); + mutex_init(&jl->j_commit_mutex); SB_JOURNAL(s)->j_num_lists++; get_journal_list(jl); return jl; @@ -4030,7 +4028,7 @@ static int do_journal_end(struct reiserfs_transaction_handle *th, * the new transaction is fully setup, and we've already flushed the * ordered bh list */ - down(&jl->j_commit_lock); + mutex_lock(&jl->j_commit_mutex); /* save the transaction id in case we need to commit it later */ commit_trans_id = jl->j_trans_id; @@ -4196,7 +4194,7 @@ static int do_journal_end(struct reiserfs_transaction_handle *th, lock_kernel(); } BUG_ON(!list_empty(&jl->j_tail_bh_list)); - up(&jl->j_commit_lock); + mutex_unlock(&jl->j_commit_mutex); /* honor the flush wishes from the caller, simple commits can ** be done outside the journal lock, they are done below diff --git a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h index c0751724ee64..315517e8bfa1 100644 --- a/include/linux/reiserfs_fs_sb.h +++ b/include/linux/reiserfs_fs_sb.h @@ -152,7 +152,7 @@ struct reiserfs_journal_list { atomic_t j_nonzerolen; atomic_t j_commit_left; atomic_t j_older_commits_done; /* all commits older than this on disk */ - struct semaphore j_commit_lock; + struct mutex j_commit_mutex; unsigned long j_trans_id; time_t j_timestamp; struct reiserfs_list_bitmap *j_list_bitmap; -- cgit From 4596c8aaf96e8634ca755c9f34b91420a39bebd4 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Fri, 25 Jul 2008 01:46:42 -0700 Subject: fat: fix VFAT_IOCTL_READDIR_xxx and cleanup for userland "struct dirent" is a kernel type here, but is a **different type** in userspace! This means both the structure and the IOCTL number is wrong! So, this adds new "struct __fat_dirent" to generate correct IOCTL number. And kernel stuff moves to under __KERNEL__. Signed-off-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/msdos_fs.h | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index 81cd36b735b0..5161394c7894 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -2,11 +2,11 @@ #define _LINUX_MSDOS_FS_H #include +#include /* * The MS-DOS filesystem constants/structures */ -#include #define SECTOR_SIZE 512 /* sector size (bytes) */ #define SECTOR_BITS 9 /* log2(SECTOR_SIZE) */ @@ -89,24 +89,22 @@ #define IS_FSINFO(x) (le32_to_cpu((x)->signature1) == FAT_FSINFO_SIG1 \ && le32_to_cpu((x)->signature2) == FAT_FSINFO_SIG2) +struct __fat_dirent { + long d_ino; + __kernel_off_t d_off; + unsigned short d_reclen; + char d_name[256]; /* We must not include limits.h! */ +}; + /* * ioctl commands */ -#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2]) -#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2]) +#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct __fat_dirent[2]) +#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct __fat_dirent[2]) /* has used 0x72 ('r') in collision, so skip a few */ #define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32) #define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, __u32) -/* - * vfat shortname flags - */ -#define VFAT_SFN_DISPLAY_LOWER 0x0001 /* convert to lowercase for display */ -#define VFAT_SFN_DISPLAY_WIN95 0x0002 /* emulate win95 rule for display */ -#define VFAT_SFN_DISPLAY_WINNT 0x0004 /* emulate winnt rule for display */ -#define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */ -#define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */ - struct fat_boot_sector { __u8 ignored[3]; /* Boot strap short or near jump */ __u8 system_id[8]; /* Name - can be used to special case @@ -168,14 +166,6 @@ struct msdos_dir_slot { __u8 name11_12[4]; /* last 2 characters in name */ }; -struct fat_slot_info { - loff_t i_pos; /* on-disk position of directory entry */ - loff_t slot_off; /* offset for slot or de start */ - int nr_slots; /* number of slots + 1(de) in filename */ - struct msdos_dir_entry *de; - struct buffer_head *bh; -}; - #ifdef __KERNEL__ #include @@ -184,6 +174,15 @@ struct fat_slot_info { #include #include +/* + * vfat shortname flags + */ +#define VFAT_SFN_DISPLAY_LOWER 0x0001 /* convert to lowercase for display */ +#define VFAT_SFN_DISPLAY_WIN95 0x0002 /* emulate win95 rule for display */ +#define VFAT_SFN_DISPLAY_WINNT 0x0004 /* emulate winnt rule for display */ +#define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */ +#define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */ + struct fat_mount_options { uid_t fs_uid; gid_t fs_gid; @@ -267,6 +266,14 @@ struct msdos_inode_info { struct inode vfs_inode; }; +struct fat_slot_info { + loff_t i_pos; /* on-disk position of directory entry */ + loff_t slot_off; /* offset for slot or de start */ + int nr_slots; /* number of slots + 1(de) in filename */ + struct msdos_dir_entry *de; + struct buffer_head *bh; +}; + static inline struct msdos_sb_info *MSDOS_SB(struct super_block *sb) { return sb->s_fs_info; -- cgit From 7557bc66be629d19a402e752673708bfbb8b5e86 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Fri, 25 Jul 2008 01:46:45 -0700 Subject: msdos fs: remove unsettable atari option It has been impossible to set the option 'atari' of the MSDOS filesystem for several years. Since nobody seems to have missed it, let's remove its remains. Signed-off-by: Rene Scharfe Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/msdos/namei.c | 18 ++++++------------ include/linux/msdos_fs.h | 1 - 2 files changed, 6 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 1f7f2956412a..e4ad6c6b753e 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -14,12 +14,7 @@ /* Characters that are undesirable in an MS-DOS file name */ static unsigned char bad_chars[] = "*?<>|\""; -static unsigned char bad_if_strict_pc[] = "+=,; "; -/* GEMDOS is less restrictive */ -static unsigned char bad_if_strict_atari[] = " "; - -#define bad_if_strict(opts) \ - ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc) +static unsigned char bad_if_strict[] = "+=,; "; /***** Formats an MS-DOS file name. Rejects invalid names. */ static int msdos_format_name(const unsigned char *name, int len, @@ -40,21 +35,20 @@ static int msdos_format_name(const unsigned char *name, int len, /* Get rid of dot - test for it elsewhere */ name++; len--; - } else if (!opts->atari) + } else return -EINVAL; } /* - * disallow names that _really_ start with a dot for MS-DOS, - * GEMDOS does not care + * disallow names that _really_ start with a dot */ - space = !opts->atari; + space = 1; c = 0; for (walk = res; len && walk - res < 8; walk++) { c = *name++; len--; if (opts->name_check != 'r' && strchr(bad_chars, c)) return -EINVAL; - if (opts->name_check == 's' && strchr(bad_if_strict(opts), c)) + if (opts->name_check == 's' && strchr(bad_if_strict, c)) return -EINVAL; if (c >= 'A' && c <= 'Z' && opts->name_check == 's') return -EINVAL; @@ -94,7 +88,7 @@ static int msdos_format_name(const unsigned char *name, int len, if (opts->name_check != 'r' && strchr(bad_chars, c)) return -EINVAL; if (opts->name_check == 's' && - strchr(bad_if_strict(opts), c)) + strchr(bad_if_strict, c)) return -EINVAL; if (c < ' ' || c == ':' || c == '\\') return -EINVAL; diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index 5161394c7894..3346c9c8f17a 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -201,7 +201,6 @@ struct fat_mount_options { utf8:1, /* Use of UTF-8 character set (Default) */ unicode_xlate:1, /* create escape sequences for unhandled Unicode */ numtail:1, /* Does first alias have a numeric '~1' type tail? */ - atari:1, /* Use Atari GEMDOS variation of MS-DOS fs */ flush:1, /* write things quickly */ nocase:1, /* Does this need case conversion? 0=need case conversion*/ usefree:1; /* Use free_clusters for FAT32 */ -- cgit From cf6ae8b50e0ee3f764392dadd1970e3f03c40773 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:46:46 -0700 Subject: remove the in-kernel struct dirent{,64} The kernel struct dirent{,64} were different from the ones in userspace. Even worse, we exported the kernel ones to userspace. But after the fat usages are fixed we can remove the conflicting kernel versions. Reviewed-by: H. Peter Anvin Signed-off-by: Adrian Bunk Cc: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/Kbuild | 1 - include/linux/dirent.h | 20 -------------------- 2 files changed, 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 71d70d1fbce2..a18008ce7aba 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -189,7 +189,6 @@ unifdef-y += connector.h unifdef-y += cuda.h unifdef-y += cyclades.h unifdef-y += dccp.h -unifdef-y += dirent.h unifdef-y += dlm.h unifdef-y += dlm_plock.h unifdef-y += edd.h diff --git a/include/linux/dirent.h b/include/linux/dirent.h index 5d6023b87800..f072fb8d10a3 100644 --- a/include/linux/dirent.h +++ b/include/linux/dirent.h @@ -1,23 +1,6 @@ #ifndef _LINUX_DIRENT_H #define _LINUX_DIRENT_H -struct dirent { - long d_ino; - __kernel_off_t d_off; - unsigned short d_reclen; - char d_name[256]; /* We must not include limits.h! */ -}; - -struct dirent64 { - __u64 d_ino; - __s64 d_off; - unsigned short d_reclen; - unsigned char d_type; - char d_name[256]; -}; - -#ifdef __KERNEL__ - struct linux_dirent64 { u64 d_ino; s64 d_off; @@ -26,7 +9,4 @@ struct linux_dirent64 { char d_name[0]; }; -#endif /* __KERNEL__ */ - - #endif -- cgit From e8938a62a85d1f487e02c3b01955b47c9598f6d2 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:46:46 -0700 Subject: remove unused #include 's Remove some unused #include 's. Signed-off-by: Adrian Bunk Cc: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/mips/kernel/linux32.c | 1 - fs/compat_ioctl.c | 1 - fs/smbfs/cache.c | 1 - fs/smbfs/proc.c | 1 - include/linux/nfsd/nfsd.h | 1 - 5 files changed, 5 deletions(-) (limited to 'include/linux') diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index c266211ed653..2fefb14414b7 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 18e2c548161d..5235c67e7594 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c index 8182f0542a21..8c177eb7e344 100644 --- a/fs/smbfs/cache.c +++ b/fs/smbfs/cache.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index d517a27b7f4b..ee536e8a649a 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index a2861d95ecc3..108f47e5fd95 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -12,7 +12,6 @@ #include #include -#include #include #include #include -- cgit From b271e067c896ad4082b15e96077675d08db40625 Mon Sep 17 00:00:00 2001 From: Joe Peterson Date: Fri, 25 Jul 2008 01:46:47 -0700 Subject: fatfs: add UTC timestamp option Provide a new mount option ("tz=UTC") for DOS (vfat/msdos) filesystems, allowing timestamps to be in coordinated universal time (UTC) rather than local time in applications where doing this is advantageous. In particular, portable devices that use fat/vfat (such as digital cameras) can benefit from using UTC in their internal clocks, thus avoiding daylight saving time errors and general time ambiguity issues. The user of the device does not have to worry about changing the time when moving from place or when daylight saving changes. The new mount option, when set, disables the counter-adjustment that Linux currently makes to FAT timestamp info in anticipation of the normal userspace time zone correction. When used in this new mode, all daylight saving time and time zone handling is done in userspace as is normal for many other filesystems (like ext3). The default mode, which remains unchanged, is still appropriate when mounting volumes written in Windows (because of its use of local time). I originally based this patch on one submitted last year by Paul Collins, but I updated it to work with current source and changed variable/option naming. Ogawa Hirofumi (who maintains these filesystems) and I discussed this patch at length on lkml, and he suggested using the option name in the attached version of the patch. Barry Bouwsma pointed out a good addition to the patch as well. Signed-off-by: Joe Peterson Signed-off-by: Paul Collins Acked-by: OGAWA Hirofumi Cc: Barry Bouwsma Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fat/dir.c | 2 +- fs/fat/inode.c | 27 ++++++++++++++++++++------- fs/fat/misc.c | 10 ++++++---- fs/msdos/namei.c | 3 ++- fs/vfat/namei.c | 2 +- include/linux/msdos_fs.h | 8 +++++--- 6 files changed, 35 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 4c35477bc94c..cd4a0162e10d 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -1101,7 +1101,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) goto error_free; } - fat_date_unix2dos(ts->tv_sec, &time, &date); + fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); de = (struct msdos_dir_entry *)bhs[0]->b_data; /* filling the new directory slots ("." and ".." entries) */ diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 60deb5fd1188..23676f9d79ce 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -382,17 +382,20 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) & ~((loff_t)sbi->cluster_size - 1)) >> 9; inode->i_mtime.tv_sec = - date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date)); + date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date), + sbi->options.tz_utc); inode->i_mtime.tv_nsec = 0; if (sbi->options.isvfat) { int secs = de->ctime_cs / 100; int csecs = de->ctime_cs % 100; inode->i_ctime.tv_sec = date_dos2unix(le16_to_cpu(de->ctime), - le16_to_cpu(de->cdate)) + secs; + le16_to_cpu(de->cdate), + sbi->options.tz_utc) + secs; inode->i_ctime.tv_nsec = csecs * 10000000; inode->i_atime.tv_sec = - date_dos2unix(0, le16_to_cpu(de->adate)); + date_dos2unix(0, le16_to_cpu(de->adate), + sbi->options.tz_utc); inode->i_atime.tv_nsec = 0; } else inode->i_ctime = inode->i_atime = inode->i_mtime; @@ -591,11 +594,14 @@ retry: raw_entry->attr = fat_attr(inode); raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart); raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16); - fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, &raw_entry->date); + fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, + &raw_entry->date, sbi->options.tz_utc); if (sbi->options.isvfat) { __le16 atime; - fat_date_unix2dos(inode->i_ctime.tv_sec,&raw_entry->ctime,&raw_entry->cdate); - fat_date_unix2dos(inode->i_atime.tv_sec,&atime,&raw_entry->adate); + fat_date_unix2dos(inode->i_ctime.tv_sec, &raw_entry->ctime, + &raw_entry->cdate, sbi->options.tz_utc); + fat_date_unix2dos(inode->i_atime.tv_sec, &atime, + &raw_entry->adate, sbi->options.tz_utc); raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 + inode->i_ctime.tv_nsec / 10000000; } @@ -836,6 +842,8 @@ static int fat_show_options(struct seq_file *m, struct vfsmount *mnt) } if (sbi->options.flush) seq_puts(m, ",flush"); + if (opts->tz_utc) + seq_puts(m, ",tz=UTC"); return 0; } @@ -848,7 +856,7 @@ enum { Opt_charset, Opt_shortname_lower, Opt_shortname_win95, Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, - Opt_obsolate, Opt_flush, Opt_err, + Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err, }; static match_table_t fat_tokens = { @@ -883,6 +891,7 @@ static match_table_t fat_tokens = { {Opt_obsolate, "cvf_options=%100s"}, {Opt_obsolate, "posix"}, {Opt_flush, "flush"}, + {Opt_tz_utc, "tz=UTC"}, {Opt_err, NULL}, }; static match_table_t msdos_tokens = { @@ -947,6 +956,7 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug, opts->utf8 = opts->unicode_xlate = 0; opts->numtail = 1; opts->usefree = opts->nocase = 0; + opts->tz_utc = 0; *debug = 0; if (!options) @@ -1036,6 +1046,9 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug, case Opt_flush: opts->flush = 1; break; + case Opt_tz_utc: + opts->tz_utc = 1; + break; /* msdos specific */ case Opt_dots: diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 61f23511eacf..79fb98ad36d4 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -142,7 +142,7 @@ static int day_n[] = { }; /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ -int date_dos2unix(unsigned short time, unsigned short date) +int date_dos2unix(unsigned short time, unsigned short date, int tz_utc) { int month, year, secs; @@ -156,16 +156,18 @@ int date_dos2unix(unsigned short time, unsigned short date) ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && month < 2 ? 1 : 0)+3653); /* days since 1.1.70 plus 80's leap day */ - secs += sys_tz.tz_minuteswest*60; + if (!tz_utc) + secs += sys_tz.tz_minuteswest*60; return secs; } /* Convert linear UNIX date to a MS-DOS time/date pair. */ -void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date) +void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, int tz_utc) { int day, year, nl_day, month; - unix_date -= sys_tz.tz_minuteswest*60; + if (!tz_utc) + unix_date -= sys_tz.tz_minuteswest*60; /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ if (unix_date < 315532800) diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index e4ad6c6b753e..e844b9809d27 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -237,6 +237,7 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name, int is_dir, int is_hid, int cluster, struct timespec *ts, struct fat_slot_info *sinfo) { + struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb); struct msdos_dir_entry de; __le16 time, date; int err; @@ -246,7 +247,7 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name, if (is_hid) de.attr |= ATTR_HIDDEN; de.lcase = 0; - fat_date_unix2dos(ts->tv_sec, &time, &date); + fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); de.cdate = de.adate = 0; de.ctime = 0; de.ctime_cs = 0; diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index b546ba69be82..155c10b4adbd 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -621,7 +621,7 @@ shortname: memcpy(de->name, msdos_name, MSDOS_NAME); de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; de->lcase = lcase; - fat_date_unix2dos(ts->tv_sec, &time, &date); + fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); de->time = de->ctime = time; de->date = de->cdate = de->adate = date; de->ctime_cs = 0; diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index 3346c9c8f17a..ba63858056c7 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -203,7 +203,8 @@ struct fat_mount_options { numtail:1, /* Does first alias have a numeric '~1' type tail? */ flush:1, /* write things quickly */ nocase:1, /* Does this need case conversion? 0=need case conversion*/ - usefree:1; /* Use free_clusters for FAT32 */ + usefree:1, /* Use free_clusters for FAT32 */ + tz_utc:1; /* Filesystem timestamps are in UTC */ }; #define FAT_HASH_BITS 8 @@ -434,8 +435,9 @@ extern int fat_flush_inodes(struct super_block *sb, struct inode *i1, extern void fat_fs_panic(struct super_block *s, const char *fmt, ...); extern void fat_clusters_flush(struct super_block *sb); extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster); -extern int date_dos2unix(unsigned short time, unsigned short date); -extern void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date); +extern int date_dos2unix(unsigned short time, unsigned short date, int tz_utc); +extern void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, + int tz_utc); extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); int fat_cache_init(void); -- cgit From b85f4b87a511bea86dac68c4f0fabaee2cac6c4c Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 25 Jul 2008 01:46:50 -0700 Subject: quota: rename quota functions from upper case, make bigger ones non-inline Cleanup quotaops.h: Rename functions from uppercase to lowercase (and define backward compatibility macros), move larger functions to dquot.c and make them non-inline. Signed-off-by: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/dquot.c | 53 +++++++++++ include/linux/quotaops.h | 226 ++++++++++++++++++++++------------------------- 2 files changed, 160 insertions(+), 119 deletions(-) (limited to 'include/linux') diff --git a/fs/dquot.c b/fs/dquot.c index ad88cf6fcbaf..0bcaf970bbb4 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -1153,6 +1153,28 @@ int dquot_drop(struct inode *inode) return 0; } +/* Wrapper to remove references to quota structures from inode */ +void vfs_dq_drop(struct inode *inode) +{ + /* Here we can get arbitrary inode from clear_inode() so we have + * to be careful. OTOH we don't need locking as quota operations + * are allowed to change only at mount time */ + if (!IS_NOQUOTA(inode) && inode->i_sb && inode->i_sb->dq_op + && inode->i_sb->dq_op->drop) { + int cnt; + /* Test before calling to rule out calls from proc and such + * where we are not allowed to block. Note that this is + * actually reliable test even without the lock - the caller + * must assure that nobody can come after the DQUOT_DROP and + * add quota pointers back anyway */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + if (inode->i_dquot[cnt] != NODQUOT) + break; + if (cnt < MAXQUOTAS) + inode->i_sb->dq_op->drop(inode); + } +} + /* * Following four functions update i_blocks+i_bytes fields and * quota information (together with appropriate checks) @@ -1426,6 +1448,18 @@ warn_put_all: return ret; } +/* Wrapper for transferring ownership of an inode */ +int vfs_dq_transfer(struct inode *inode, struct iattr *iattr) +{ + if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) { + vfs_dq_init(inode); + if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA) + return 1; + } + return 0; +} + + /* * Write info of quota file to disk */ @@ -1766,6 +1800,22 @@ out: return error; } +/* Wrapper to turn on quotas when remounting rw */ +int vfs_dq_quota_on_remount(struct super_block *sb) +{ + int cnt; + int ret = 0, err; + + if (!sb->s_qcop || !sb->s_qcop->quota_on) + return -ENOSYS; + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + err = sb->s_qcop->quota_on(sb, cnt, 0, NULL, 1); + if (err < 0 && !ret) + ret = err; + } + return ret; +} + /* Generic routine for getting common part of quota structure */ static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di) { @@ -2101,8 +2151,11 @@ EXPORT_SYMBOL(dquot_release); EXPORT_SYMBOL(dquot_mark_dquot_dirty); EXPORT_SYMBOL(dquot_initialize); EXPORT_SYMBOL(dquot_drop); +EXPORT_SYMBOL(vfs_dq_drop); EXPORT_SYMBOL(dquot_alloc_space); EXPORT_SYMBOL(dquot_alloc_inode); EXPORT_SYMBOL(dquot_free_space); EXPORT_SYMBOL(dquot_free_inode); EXPORT_SYMBOL(dquot_transfer); +EXPORT_SYMBOL(vfs_dq_transfer); +EXPORT_SYMBOL(vfs_dq_quota_on_remount); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index f86702053853..0c8f9fe462af 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -19,34 +19,38 @@ /* * declaration of quota_function calls in kernel. */ -extern void sync_dquots(struct super_block *sb, int type); - -extern int dquot_initialize(struct inode *inode, int type); -extern int dquot_drop(struct inode *inode); - -extern int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc); -extern int dquot_alloc_inode(const struct inode *inode, unsigned long number); - -extern int dquot_free_space(struct inode *inode, qsize_t number); -extern int dquot_free_inode(const struct inode *inode, unsigned long number); - -extern int dquot_transfer(struct inode *inode, struct iattr *iattr); -extern int dquot_commit(struct dquot *dquot); -extern int dquot_acquire(struct dquot *dquot); -extern int dquot_release(struct dquot *dquot); -extern int dquot_commit_info(struct super_block *sb, int type); -extern int dquot_mark_dquot_dirty(struct dquot *dquot); - -extern int vfs_quota_on(struct super_block *sb, int type, int format_id, - char *path, int remount); -extern int vfs_quota_on_mount(struct super_block *sb, char *qf_name, - int format_id, int type); -extern int vfs_quota_off(struct super_block *sb, int type, int remount); -extern int vfs_quota_sync(struct super_block *sb, int type); -extern int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); -extern int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); -extern int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di); -extern int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di); +void sync_dquots(struct super_block *sb, int type); + +int dquot_initialize(struct inode *inode, int type); +int dquot_drop(struct inode *inode); + +int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc); +int dquot_alloc_inode(const struct inode *inode, unsigned long number); + +int dquot_free_space(struct inode *inode, qsize_t number); +int dquot_free_inode(const struct inode *inode, unsigned long number); + +int dquot_transfer(struct inode *inode, struct iattr *iattr); +int dquot_commit(struct dquot *dquot); +int dquot_acquire(struct dquot *dquot); +int dquot_release(struct dquot *dquot); +int dquot_commit_info(struct super_block *sb, int type); +int dquot_mark_dquot_dirty(struct dquot *dquot); + +int vfs_quota_on(struct super_block *sb, int type, int format_id, + char *path, int remount); +int vfs_quota_on_mount(struct super_block *sb, char *qf_name, + int format_id, int type); +int vfs_quota_off(struct super_block *sb, int type, int remount); +int vfs_quota_sync(struct super_block *sb, int type); +int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); +int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii); +int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di); +int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di); + +void vfs_dq_drop(struct inode *inode); +int vfs_dq_transfer(struct inode *inode, struct iattr *iattr); +int vfs_dq_quota_on_remount(struct super_block *sb); /* * Operations supported for diskquotas. @@ -59,38 +63,16 @@ extern struct quotactl_ops vfs_quotactl_ops; /* It is better to call this function outside of any transaction as it might * need a lot of space in journal for dquot structure allocation. */ -static inline void DQUOT_INIT(struct inode *inode) +static inline void vfs_dq_init(struct inode *inode) { BUG_ON(!inode->i_sb); if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) inode->i_sb->dq_op->initialize(inode, -1); } -/* The same as with DQUOT_INIT */ -static inline void DQUOT_DROP(struct inode *inode) -{ - /* Here we can get arbitrary inode from clear_inode() so we have - * to be careful. OTOH we don't need locking as quota operations - * are allowed to change only at mount time */ - if (!IS_NOQUOTA(inode) && inode->i_sb && inode->i_sb->dq_op - && inode->i_sb->dq_op->drop) { - int cnt; - /* Test before calling to rule out calls from proc and such - * where we are not allowed to block. Note that this is - * actually reliable test even without the lock - the caller - * must assure that nobody can come after the DQUOT_DROP and - * add quota pointers back anyway */ - for (cnt = 0; cnt < MAXQUOTAS; cnt++) - if (inode->i_dquot[cnt] != NODQUOT) - break; - if (cnt < MAXQUOTAS) - inode->i_sb->dq_op->drop(inode); - } -} - /* The following allocation/freeing/transfer functions *must* be called inside * a transaction (deadlocks possible otherwise) */ -static inline int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_space_nodirty(struct inode *inode, qsize_t nr) { if (sb_any_quota_enabled(inode->i_sb)) { /* Used space is updated in alloc_space() */ @@ -102,15 +84,15 @@ static inline int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) return 0; } -static inline int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_space(struct inode *inode, qsize_t nr) { int ret; - if (!(ret = DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr))) + if (!(ret = vfs_dq_prealloc_space_nodirty(inode, nr))) mark_inode_dirty(inode); return ret; } -static inline int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_space_nodirty(struct inode *inode, qsize_t nr) { if (sb_any_quota_enabled(inode->i_sb)) { /* Used space is updated in alloc_space() */ @@ -122,25 +104,25 @@ static inline int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) return 0; } -static inline int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr) { int ret; - if (!(ret = DQUOT_ALLOC_SPACE_NODIRTY(inode, nr))) + if (!(ret = vfs_dq_alloc_space_nodirty(inode, nr))) mark_inode_dirty(inode); return ret; } -static inline int DQUOT_ALLOC_INODE(struct inode *inode) +static inline int vfs_dq_alloc_inode(struct inode *inode) { if (sb_any_quota_enabled(inode->i_sb)) { - DQUOT_INIT(inode); + vfs_dq_init(inode); if (inode->i_sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) return 1; } return 0; } -static inline void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr) { if (sb_any_quota_enabled(inode->i_sb)) inode->i_sb->dq_op->free_space(inode, nr); @@ -148,35 +130,25 @@ static inline void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr) inode_sub_bytes(inode, nr); } -static inline void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_space(struct inode *inode, qsize_t nr) { - DQUOT_FREE_SPACE_NODIRTY(inode, nr); + vfs_dq_free_space_nodirty(inode, nr); mark_inode_dirty(inode); } -static inline void DQUOT_FREE_INODE(struct inode *inode) +static inline void vfs_dq_free_inode(struct inode *inode) { if (sb_any_quota_enabled(inode->i_sb)) inode->i_sb->dq_op->free_inode(inode, 1); } -static inline int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr) -{ - if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) { - DQUOT_INIT(inode); - if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA) - return 1; - } - return 0; -} - /* The following two functions cannot be called inside a transaction */ -static inline void DQUOT_SYNC(struct super_block *sb) +static inline void vfs_dq_sync(struct super_block *sb) { sync_dquots(sb, -1); } -static inline int DQUOT_OFF(struct super_block *sb, int remount) +static inline int vfs_dq_off(struct super_block *sb, int remount) { int ret = -ENOSYS; @@ -185,21 +157,6 @@ static inline int DQUOT_OFF(struct super_block *sb, int remount) return ret; } -static inline int DQUOT_ON_REMOUNT(struct super_block *sb) -{ - int cnt; - int ret = 0, err; - - if (!sb->s_qcop || !sb->s_qcop->quota_on) - return -ENOSYS; - for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - err = sb->s_qcop->quota_on(sb, cnt, 0, NULL, 1); - if (err < 0 && !ret) - ret = err; - } - return ret; -} - #else /* @@ -208,113 +165,144 @@ static inline int DQUOT_ON_REMOUNT(struct super_block *sb) #define sb_dquot_ops (NULL) #define sb_quotactl_ops (NULL) -static inline void DQUOT_INIT(struct inode *inode) +static inline void vfs_dq_init(struct inode *inode) { } -static inline void DQUOT_DROP(struct inode *inode) +static inline void vfs_dq_drop(struct inode *inode) { } -static inline int DQUOT_ALLOC_INODE(struct inode *inode) +static inline int vfs_dq_alloc_inode(struct inode *inode) { return 0; } -static inline void DQUOT_FREE_INODE(struct inode *inode) +static inline void vfs_dq_free_inode(struct inode *inode) { } -static inline void DQUOT_SYNC(struct super_block *sb) +static inline void vfs_dq_sync(struct super_block *sb) { } -static inline int DQUOT_OFF(struct super_block *sb, int remount) +static inline int vfs_dq_off(struct super_block *sb, int remount) { return 0; } -static inline int DQUOT_ON_REMOUNT(struct super_block *sb) +static inline int vfs_dq_quota_on_remount(struct super_block *sb) { return 0; } -static inline int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr) +static inline int vfs_dq_transfer(struct inode *inode, struct iattr *iattr) { return 0; } -static inline int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_space_nodirty(struct inode *inode, qsize_t nr) { inode_add_bytes(inode, nr); return 0; } -static inline int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_space(struct inode *inode, qsize_t nr) { - DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr); + vfs_dq_prealloc_space_nodirty(inode, nr); mark_inode_dirty(inode); return 0; } -static inline int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_space_nodirty(struct inode *inode, qsize_t nr) { inode_add_bytes(inode, nr); return 0; } -static inline int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr) { - DQUOT_ALLOC_SPACE_NODIRTY(inode, nr); + vfs_dq_alloc_space_nodirty(inode, nr); mark_inode_dirty(inode); return 0; } -static inline void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr) { inode_sub_bytes(inode, nr); } -static inline void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_space(struct inode *inode, qsize_t nr) { - DQUOT_FREE_SPACE_NODIRTY(inode, nr); + vfs_dq_free_space_nodirty(inode, nr); mark_inode_dirty(inode); } #endif /* CONFIG_QUOTA */ -static inline int DQUOT_PREALLOC_BLOCK_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_block_nodirty(struct inode *inode, qsize_t nr) { - return DQUOT_PREALLOC_SPACE_NODIRTY(inode, + return vfs_dq_prealloc_space_nodirty(inode, nr << inode->i_sb->s_blocksize_bits); } -static inline int DQUOT_PREALLOC_BLOCK(struct inode *inode, qsize_t nr) +static inline int vfs_dq_prealloc_block(struct inode *inode, qsize_t nr) { - return DQUOT_PREALLOC_SPACE(inode, + return vfs_dq_prealloc_space(inode, nr << inode->i_sb->s_blocksize_bits); } -static inline int DQUOT_ALLOC_BLOCK_NODIRTY(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_block_nodirty(struct inode *inode, qsize_t nr) { - return DQUOT_ALLOC_SPACE_NODIRTY(inode, + return vfs_dq_alloc_space_nodirty(inode, nr << inode->i_sb->s_blocksize_bits); } -static inline int DQUOT_ALLOC_BLOCK(struct inode *inode, qsize_t nr) +static inline int vfs_dq_alloc_block(struct inode *inode, qsize_t nr) { - return DQUOT_ALLOC_SPACE(inode, + return vfs_dq_alloc_space(inode, nr << inode->i_sb->s_blocksize_bits); } -static inline void DQUOT_FREE_BLOCK_NODIRTY(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_block_nodirty(struct inode *inode, qsize_t nr) { - DQUOT_FREE_SPACE_NODIRTY(inode, nr << inode->i_sb->s_blocksize_bits); + vfs_dq_free_space_nodirty(inode, nr << inode->i_sb->s_blocksize_bits); } -static inline void DQUOT_FREE_BLOCK(struct inode *inode, qsize_t nr) +static inline void vfs_dq_free_block(struct inode *inode, qsize_t nr) { - DQUOT_FREE_SPACE(inode, nr << inode->i_sb->s_blocksize_bits); + vfs_dq_free_space(inode, nr << inode->i_sb->s_blocksize_bits); } +/* + * Define uppercase equivalents for compatibility with old function names + * Can go away when we think all users have been converted (15/04/2008) + */ +#define DQUOT_INIT(inode) vfs_dq_init(inode) +#define DQUOT_DROP(inode) vfs_dq_drop(inode) +#define DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr) \ + vfs_dq_prealloc_space_nodirty(inode, nr) +#define DQUOT_PREALLOC_SPACE(inode, nr) vfs_dq_prealloc_space(inode, nr) +#define DQUOT_ALLOC_SPACE_NODIRTY(inode, nr) \ + vfs_dq_alloc_space_nodirty(inode, nr) +#define DQUOT_ALLOC_SPACE(inode, nr) vfs_dq_alloc_space(inode, nr) +#define DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr) \ + vfs_dq_prealloc_block_nodirty(inode, nr) +#define DQUOT_PREALLOC_BLOCK(inode, nr) vfs_dq_prealloc_block(inode, nr) +#define DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr) \ + vfs_dq_alloc_block_nodirty(inode, nr) +#define DQUOT_ALLOC_BLOCK(inode, nr) vfs_dq_alloc_block(inode, nr) +#define DQUOT_ALLOC_INODE(inode) vfs_dq_alloc_inode(inode) +#define DQUOT_FREE_SPACE_NODIRTY(inode, nr) \ + vfs_dq_free_space_nodirty(inode, nr) +#define DQUOT_FREE_SPACE(inode, nr) vfs_dq_free_space(inode, nr) +#define DQUOT_FREE_BLOCK_NODIRTY(inode, nr) \ + vfs_dq_free_block_nodirty(inode, nr) +#define DQUOT_FREE_BLOCK(inode, nr) vfs_dq_free_block(inode, nr) +#define DQUOT_FREE_INODE(inode) vfs_dq_free_inode(inode) +#define DQUOT_TRANSFER(inode, iattr) vfs_dq_transfer(inode, iattr) +#define DQUOT_SYNC(sb) vfs_dq_sync(sb) +#define DQUOT_OFF(sb, remount) vfs_dq_off(sb, remount) +#define DQUOT_ON_REMOUNT(sb) vfs_dq_quota_on_remount(sb) + #endif /* _LINUX_QUOTAOPS_ */ -- cgit From 02a55ca87185e114e5d298a8d00608501dbabf67 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 25 Jul 2008 01:46:50 -0700 Subject: quota: cleanup loop in sync_dquots() Make loop in sync_dquots() checking whether there's something to write more readable, remove useless variable and macro info_any_dirty() which is used only in this place. Signed-off-by: Jan Kara Cc: "Vegard Nossum" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/quota.c | 18 ++++++++++++------ include/linux/quota.h | 2 -- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/quota.c b/fs/quota.c index db1cc9f3c7aa..7f4386ebc23a 100644 --- a/fs/quota.c +++ b/fs/quota.c @@ -186,7 +186,7 @@ static void quota_sync_sb(struct super_block *sb, int type) void sync_dquots(struct super_block *sb, int type) { - int cnt, dirty; + int cnt; if (sb) { if (sb->s_qcop->quota_sync) @@ -198,11 +198,17 @@ void sync_dquots(struct super_block *sb, int type) restart: list_for_each_entry(sb, &super_blocks, s_list) { /* This test just improves performance so it needn't be reliable... */ - for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++) - if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt) - && info_any_dirty(&sb_dqopt(sb)->info[cnt])) - dirty = 1; - if (!dirty) + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (type != -1 && type != cnt) + continue; + if (!sb_has_quota_enabled(sb, cnt)) + continue; + if (!info_dirty(&sb_dqopt(sb)->info[cnt]) && + list_empty(&sb_dqopt(sb)->info[cnt].dqi_dirty_list)) + continue; + break; + } + if (cnt == MAXQUOTAS) continue; sb->s_count++; spin_unlock(&sb_lock); diff --git a/include/linux/quota.h b/include/linux/quota.h index dcddfb200947..6f1d97ddf828 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -224,8 +224,6 @@ struct super_block; extern void mark_info_dirty(struct super_block *sb, int type); #define info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags) -#define info_any_dquot_dirty(info) (!list_empty(&(info)->dqi_dirty_list)) -#define info_any_dirty(info) (info_dirty(info) || info_any_dquot_dirty(info)) #define sb_dqopt(sb) (&(sb)->s_dquot) #define sb_dqinfo(sb, type) (sb_dqopt(sb)->info+(type)) -- cgit From 74abb9890dafb12a50dc140de215ed477beb1b88 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 25 Jul 2008 01:46:51 -0700 Subject: quota: move function-macros from quota.h to quotaops.h Move declarations of some macros, which should be in fact functions to quotaops.h. This way they can be later converted to inline functions because we can now use declarations from quota.h. Also add necessary includes of quotaops.h to a few files. [akpm@linux-foundation.org: fix JFS build] [akpm@linux-foundation.org: fix UFS build] [vegard.nossum@gmail.com: fix QUOTA=n build] Signed-off-by: Jan Kara Cc: Vegard Nossum Cc: Arjen Pool Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ext2/super.c | 1 + fs/jfs/super.c | 1 + fs/quota_v1.c | 1 + fs/quota_v2.c | 1 + fs/reiserfs/super.c | 1 + fs/ufs/super.c | 1 + include/linux/quota.h | 22 +++------------------- include/linux/quotaops.h | 26 ++++++++++++++++++++++++++ 8 files changed, 35 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/fs/ext2/super.c b/fs/ext2/super.c index ef50cbc792db..31308a3b0b8b 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "ext2.h" #include "xattr.h" diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 0288e6d7936a..359c091d8965 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/quota_v1.c b/fs/quota_v1.c index a6cf9269105c..5ae15b13eeb0 100644 --- a/fs/quota_v1.c +++ b/fs/quota_v1.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/quota_v2.c b/fs/quota_v2.c index 234ada903633..b53827dc02d9 100644 --- a/fs/quota_v2.c +++ b/fs/quota_v2.c @@ -11,6 +11,7 @@ #include #include #include +#include #include diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index a10a6d2a8870..2ec748ba0bd3 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 506f724055c2..227c9d700040 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -76,6 +76,7 @@ #include #include +#include #include #include #include diff --git a/include/linux/quota.h b/include/linux/quota.h index 6f1d97ddf828..f9983ea0ff88 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -41,9 +41,6 @@ #define __DQUOT_VERSION__ "dquot_6.5.1" #define __DQUOT_NUM_VERSION__ 6*10000+5*100+1 -typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */ -typedef __u64 qsize_t; /* Type in which we store sizes */ - /* Size of blocks in which are counted size limits */ #define QUOTABLOCK_BITS 10 #define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS) @@ -172,6 +169,9 @@ enum { #include +typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */ +typedef __u64 qsize_t; /* Type in which we store sizes */ + extern spinlock_t dq_data_lock; /* Maximal numbers of writes for quota operation (insert/delete/update) @@ -225,9 +225,6 @@ struct super_block; extern void mark_info_dirty(struct super_block *sb, int type); #define info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags) -#define sb_dqopt(sb) (&(sb)->s_dquot) -#define sb_dqinfo(sb, type) (sb_dqopt(sb)->info+(type)) - struct dqstats { int lookups; int drops; @@ -335,19 +332,6 @@ struct quota_info { struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */ }; -#define sb_has_quota_enabled(sb, type) ((type)==USRQUOTA ? \ - (sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) : (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED)) - -#define sb_any_quota_enabled(sb) (sb_has_quota_enabled(sb, USRQUOTA) | \ - sb_has_quota_enabled(sb, GRPQUOTA)) - -#define sb_has_quota_suspended(sb, type) \ - ((type) == USRQUOTA ? (sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED) : \ - (sb_dqopt(sb)->flags & DQUOT_GRP_SUSPENDED)) - -#define sb_any_quota_suspended(sb) (sb_has_quota_suspended(sb, USRQUOTA) | \ - sb_has_quota_suspended(sb, GRPQUOTA)) - int register_quota_format(struct quota_format_type *fmt); void unregister_quota_format(struct quota_format_type *fmt); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 0c8f9fe462af..38218c1334b1 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -14,6 +14,8 @@ #include +#define sb_dqopt(sb) (&(sb)->s_dquot) + #if defined(CONFIG_QUOTA) /* @@ -52,6 +54,25 @@ void vfs_dq_drop(struct inode *inode); int vfs_dq_transfer(struct inode *inode, struct iattr *iattr); int vfs_dq_quota_on_remount(struct super_block *sb); +#define sb_dqinfo(sb, type) (sb_dqopt(sb)->info+(type)) + +/* + * Functions for checking status of quota + */ + +#define sb_has_quota_enabled(sb, type) ((type)==USRQUOTA ? \ + (sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) : (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED)) + +#define sb_any_quota_enabled(sb) (sb_has_quota_enabled(sb, USRQUOTA) | \ + sb_has_quota_enabled(sb, GRPQUOTA)) + +#define sb_has_quota_suspended(sb, type) \ + ((type) == USRQUOTA ? (sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED) : \ + (sb_dqopt(sb)->flags & DQUOT_GRP_SUSPENDED)) + +#define sb_any_quota_suspended(sb) (sb_has_quota_suspended(sb, USRQUOTA) | \ + sb_has_quota_suspended(sb, GRPQUOTA)) + /* * Operations supported for diskquotas. */ @@ -159,6 +180,11 @@ static inline int vfs_dq_off(struct super_block *sb, int remount) #else +#define sb_has_quota_enabled(sb, type) 0 +#define sb_any_quota_enabled(sb) 0 +#define sb_has_quota_suspended(sb, type) 0 +#define sb_any_quota_suspended(sb) 0 + /* * NO-OP when quota not configured. */ -- cgit From 03b063436ca1076301de58d9d628f610ab5404ad Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 25 Jul 2008 01:46:52 -0700 Subject: quota: convert macros to inline functions Signed-off-by: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/quota.h | 5 +++- include/linux/quotaops.h | 65 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/quota.h b/include/linux/quota.h index f9983ea0ff88..4e004fef8134 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -223,7 +223,10 @@ struct super_block; #define DQF_INFO_DIRTY (1 << DQF_INFO_DIRTY_B) /* Is info dirty? */ extern void mark_info_dirty(struct super_block *sb, int type); -#define info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags) +static inline int info_dirty(struct mem_dqinfo *info) +{ + return test_bit(DQF_INFO_DIRTY_B, &info->dqi_flags); +} struct dqstats { int lookups; diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 38218c1334b1..742187f7a05c 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -11,10 +11,12 @@ #define _LINUX_QUOTAOPS_ #include - #include -#define sb_dqopt(sb) (&(sb)->s_dquot) +static inline struct quota_info *sb_dqopt(struct super_block *sb) +{ + return &sb->s_dquot; +} #if defined(CONFIG_QUOTA) @@ -54,24 +56,40 @@ void vfs_dq_drop(struct inode *inode); int vfs_dq_transfer(struct inode *inode, struct iattr *iattr); int vfs_dq_quota_on_remount(struct super_block *sb); -#define sb_dqinfo(sb, type) (sb_dqopt(sb)->info+(type)) +static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type) +{ + return sb_dqopt(sb)->info + type; +} /* * Functions for checking status of quota */ -#define sb_has_quota_enabled(sb, type) ((type)==USRQUOTA ? \ - (sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) : (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED)) +static inline int sb_has_quota_enabled(struct super_block *sb, int type) +{ + if (type == USRQUOTA) + return sb_dqopt(sb)->flags & DQUOT_USR_ENABLED; + return sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED; +} -#define sb_any_quota_enabled(sb) (sb_has_quota_enabled(sb, USRQUOTA) | \ - sb_has_quota_enabled(sb, GRPQUOTA)) +static inline int sb_any_quota_enabled(struct super_block *sb) +{ + return sb_has_quota_enabled(sb, USRQUOTA) || + sb_has_quota_enabled(sb, GRPQUOTA); +} -#define sb_has_quota_suspended(sb, type) \ - ((type) == USRQUOTA ? (sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED) : \ - (sb_dqopt(sb)->flags & DQUOT_GRP_SUSPENDED)) +static inline int sb_has_quota_suspended(struct super_block *sb, int type) +{ + if (type == USRQUOTA) + return sb_dqopt(sb)->flags & DQUOT_USR_SUSPENDED; + return sb_dqopt(sb)->flags & DQUOT_GRP_SUSPENDED; +} -#define sb_any_quota_suspended(sb) (sb_has_quota_suspended(sb, USRQUOTA) | \ - sb_has_quota_suspended(sb, GRPQUOTA)) +static inline int sb_any_quota_suspended(struct super_block *sb) +{ + return sb_has_quota_suspended(sb, USRQUOTA) || + sb_has_quota_suspended(sb, GRPQUOTA); +} /* * Operations supported for diskquotas. @@ -180,10 +198,25 @@ static inline int vfs_dq_off(struct super_block *sb, int remount) #else -#define sb_has_quota_enabled(sb, type) 0 -#define sb_any_quota_enabled(sb) 0 -#define sb_has_quota_suspended(sb, type) 0 -#define sb_any_quota_suspended(sb) 0 +static inline int sb_has_quota_enabled(struct super_block *sb, int type) +{ + return 0; +} + +static inline int sb_any_quota_enabled(struct super_block *sb) +{ + return 0; +} + +static inline int sb_has_quota_suspended(struct super_block *sb, int type) +{ + return 0; +} + +static inline int sb_any_quota_suspended(struct super_block *sb) +{ + return 0; +} /* * NO-OP when quota not configured. -- cgit From 657d3bfa98e542271b449f8cd84c7501ae2b2255 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 25 Jul 2008 01:46:52 -0700 Subject: quota: implement sending information via netlink about user below quota Sometimes it may be useful for userspace to know (e.g. for some hosting guys) that some user stopped exceeding his hardlimit or softlimit in quotas. Implement sending of such events to userspace via quota netlink protocol so that they don't have to poll for such events. Based on idea and initial implementation by Vladislav Bogdanov. Cc: Vladislav Bogdanov Signed-off-by: Jan Kara Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/dquot.c | 60 +++++++++++++++++++++++++++++++++++++++++++++------ include/linux/quota.h | 4 ++++ 2 files changed, 58 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/dquot.c b/fs/dquot.c index 0bcaf970bbb4..1346eebe74ce 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -889,7 +889,10 @@ static void print_warning(struct dquot *dquot, const int warntype) char *msg = NULL; struct tty_struct *tty; - if (!need_print_warning(dquot)) + if (warntype == QUOTA_NL_IHARDBELOW || + warntype == QUOTA_NL_ISOFTBELOW || + warntype == QUOTA_NL_BHARDBELOW || + warntype == QUOTA_NL_BSOFTBELOW || !need_print_warning(dquot)) return; mutex_lock(&tty_mutex); @@ -1097,6 +1100,35 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war return QUOTA_OK; } +static int info_idq_free(struct dquot *dquot, ulong inodes) +{ + if (test_bit(DQ_FAKE_B, &dquot->dq_flags) || + dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit) + return QUOTA_NL_NOWARN; + + if (dquot->dq_dqb.dqb_curinodes - inodes <= dquot->dq_dqb.dqb_isoftlimit) + return QUOTA_NL_ISOFTBELOW; + if (dquot->dq_dqb.dqb_curinodes >= dquot->dq_dqb.dqb_ihardlimit && + dquot->dq_dqb.dqb_curinodes - inodes < dquot->dq_dqb.dqb_ihardlimit) + return QUOTA_NL_IHARDBELOW; + return QUOTA_NL_NOWARN; +} + +static int info_bdq_free(struct dquot *dquot, qsize_t space) +{ + if (test_bit(DQ_FAKE_B, &dquot->dq_flags) || + toqb(dquot->dq_dqb.dqb_curspace) <= dquot->dq_dqb.dqb_bsoftlimit) + return QUOTA_NL_NOWARN; + + if (toqb(dquot->dq_dqb.dqb_curspace - space) <= + dquot->dq_dqb.dqb_bsoftlimit) + return QUOTA_NL_BSOFTBELOW; + if (toqb(dquot->dq_dqb.dqb_curspace) >= dquot->dq_dqb.dqb_bhardlimit && + toqb(dquot->dq_dqb.dqb_curspace - space) < + dquot->dq_dqb.dqb_bhardlimit) + return QUOTA_NL_BHARDBELOW; + return QUOTA_NL_NOWARN; +} /* * Initialize quota pointers in inode * Transaction must be started at entry @@ -1284,6 +1316,7 @@ warn_put_all: int dquot_free_space(struct inode *inode, qsize_t number) { unsigned int cnt; + char warntype[MAXQUOTAS]; /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ @@ -1292,6 +1325,7 @@ out_sub: inode_sub_bytes(inode, number); return QUOTA_OK; } + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); /* Now recheck reliably when holding dqptr_sem */ if (IS_NOQUOTA(inode)) { @@ -1302,6 +1336,7 @@ out_sub: for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; + warntype[cnt] = info_bdq_free(inode->i_dquot[cnt], number); dquot_decr_space(inode->i_dquot[cnt], number); } inode_sub_bytes(inode, number); @@ -1310,6 +1345,7 @@ out_sub: for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (inode->i_dquot[cnt]) mark_dquot_dirty(inode->i_dquot[cnt]); + flush_warnings(inode->i_dquot, warntype); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); return QUOTA_OK; } @@ -1320,11 +1356,13 @@ out_sub: int dquot_free_inode(const struct inode *inode, unsigned long number) { unsigned int cnt; + char warntype[MAXQUOTAS]; /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ if (IS_NOQUOTA(inode)) return QUOTA_OK; + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); /* Now recheck reliably when holding dqptr_sem */ if (IS_NOQUOTA(inode)) { @@ -1335,6 +1373,7 @@ int dquot_free_inode(const struct inode *inode, unsigned long number) for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; + warntype[cnt] = info_idq_free(inode->i_dquot[cnt], number); dquot_decr_inodes(inode->i_dquot[cnt], number); } spin_unlock(&dq_data_lock); @@ -1342,6 +1381,7 @@ int dquot_free_inode(const struct inode *inode, unsigned long number) for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (inode->i_dquot[cnt]) mark_dquot_dirty(inode->i_dquot[cnt]); + flush_warnings(inode->i_dquot, warntype); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); return QUOTA_OK; } @@ -1359,7 +1399,8 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) struct dquot *transfer_to[MAXQUOTAS]; int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid, chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid; - char warntype[MAXQUOTAS]; + char warntype_to[MAXQUOTAS]; + char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS]; /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ @@ -1368,7 +1409,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) /* Clear the arrays */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { transfer_to[cnt] = transfer_from[cnt] = NODQUOT; - warntype[cnt] = QUOTA_NL_NOWARN; + warntype_to[cnt] = QUOTA_NL_NOWARN; } down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); /* Now recheck reliably when holding dqptr_sem */ @@ -1400,8 +1441,9 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) if (transfer_to[cnt] == NODQUOT) continue; transfer_from[cnt] = inode->i_dquot[cnt]; - if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA || - check_bdq(transfer_to[cnt], space, 0, warntype+cnt) == NO_QUOTA) + if (check_idq(transfer_to[cnt], 1, warntype_to + cnt) == + NO_QUOTA || check_bdq(transfer_to[cnt], space, 0, + warntype_to + cnt) == NO_QUOTA) goto warn_put_all; } @@ -1417,6 +1459,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) /* Due to IO error we might not have transfer_from[] structure */ if (transfer_from[cnt]) { + warntype_from_inodes[cnt] = + info_idq_free(transfer_from[cnt], 1); + warntype_from_space[cnt] = + info_bdq_free(transfer_from[cnt], space); dquot_decr_inodes(transfer_from[cnt], 1); dquot_decr_space(transfer_from[cnt], space); } @@ -1436,7 +1482,9 @@ warn_put_all: if (transfer_to[cnt]) mark_dquot_dirty(transfer_to[cnt]); } - flush_warnings(transfer_to, warntype); + flush_warnings(transfer_to, warntype_to); + flush_warnings(transfer_from, warntype_from_inodes); + flush_warnings(transfer_from, warntype_from_space); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (ret == QUOTA_OK && transfer_from[cnt] != NODQUOT) diff --git a/include/linux/quota.h b/include/linux/quota.h index 4e004fef8134..376a05048bc5 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -135,6 +135,10 @@ struct if_dqinfo { #define QUOTA_NL_BHARDWARN 4 /* Block hardlimit reached */ #define QUOTA_NL_BSOFTLONGWARN 5 /* Block grace time expired */ #define QUOTA_NL_BSOFTWARN 6 /* Block softlimit reached */ +#define QUOTA_NL_IHARDBELOW 7 /* Usage got below inode hardlimit */ +#define QUOTA_NL_ISOFTBELOW 8 /* Usage got below inode softlimit */ +#define QUOTA_NL_BHARDBELOW 9 /* Usage got below block hardlimit */ +#define QUOTA_NL_BSOFTBELOW 10 /* Usage got below block softlimit */ enum { QUOTA_NL_C_UNSPEC, -- cgit From f2992db2a4f7ae10f61d5bc68c7c1528cec639e2 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:46:55 -0700 Subject: Mark res_counter_charge(_locked) with __must_check Ignoring their return values may result in counter underflow in the future - when the value charged will be uncharged (or in "leaks" - when the value is not uncharged). This also prevents from using charging routines to decrement the counter value (i.e. uncharge it) ;) (Current code works OK with res_counter, however :) ) Signed-off-by: Pavel Emelyanov Cc: Balbir Singh Cc: Paul Menage Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/res_counter.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index 6d9e1fca098c..125660e7793f 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -95,8 +95,10 @@ void res_counter_init(struct res_counter *counter); * counter->limit _locked call expects the counter->lock to be taken */ -int res_counter_charge_locked(struct res_counter *counter, unsigned long val); -int res_counter_charge(struct res_counter *counter, unsigned long val); +int __must_check res_counter_charge_locked(struct res_counter *counter, + unsigned long val); +int __must_check res_counter_charge(struct res_counter *counter, + unsigned long val); /* * uncharge - tell that some portion of the resource is released -- cgit From ce16b49d37e748574f7fabc2726268d542d0aa1a Mon Sep 17 00:00:00 2001 From: Paul Menage Date: Fri, 25 Jul 2008 01:46:57 -0700 Subject: cgroup files: clean up whitespace in struct cftype This patch removes some extraneous spaces from method declarations in struct cftype, to fit in with conventional kernel style. Signed-off-by: Paul Menage Cc: Paul Jackson Cc: Pavel Emelyanov Cc: Balbir Singh Cc: Serge Hallyn Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index e155aa78d859..88a734edccbc 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -205,48 +205,48 @@ struct cftype { * subsystem, followed by a period */ char name[MAX_CFTYPE_NAME]; int private; - int (*open) (struct inode *inode, struct file *file); - ssize_t (*read) (struct cgroup *cgrp, struct cftype *cft, - struct file *file, - char __user *buf, size_t nbytes, loff_t *ppos); + int (*open)(struct inode *inode, struct file *file); + ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, + struct file *file, + char __user *buf, size_t nbytes, loff_t *ppos); /* * read_u64() is a shortcut for the common case of returning a * single integer. Use it in place of read() */ - u64 (*read_u64) (struct cgroup *cgrp, struct cftype *cft); + u64 (*read_u64)(struct cgroup *cgrp, struct cftype *cft); /* * read_s64() is a signed version of read_u64() */ - s64 (*read_s64) (struct cgroup *cgrp, struct cftype *cft); + s64 (*read_s64)(struct cgroup *cgrp, struct cftype *cft); /* * read_map() is used for defining a map of key/value * pairs. It should call cb->fill(cb, key, value) for each * entry. The key/value pairs (and their ordering) should not * change between reboots. */ - int (*read_map) (struct cgroup *cont, struct cftype *cft, - struct cgroup_map_cb *cb); + int (*read_map)(struct cgroup *cont, struct cftype *cft, + struct cgroup_map_cb *cb); /* * read_seq_string() is used for outputting a simple sequence * using seqfile. */ - int (*read_seq_string) (struct cgroup *cont, struct cftype *cft, - struct seq_file *m); + int (*read_seq_string)(struct cgroup *cont, struct cftype *cft, + struct seq_file *m); - ssize_t (*write) (struct cgroup *cgrp, struct cftype *cft, - struct file *file, - const char __user *buf, size_t nbytes, loff_t *ppos); + ssize_t (*write)(struct cgroup *cgrp, struct cftype *cft, + struct file *file, + const char __user *buf, size_t nbytes, loff_t *ppos); /* * write_u64() is a shortcut for the common case of accepting * a single integer (as parsed by simple_strtoull) from * userspace. Use in place of write(); return 0 or error. */ - int (*write_u64) (struct cgroup *cgrp, struct cftype *cft, u64 val); + int (*write_u64)(struct cgroup *cgrp, struct cftype *cft, u64 val); /* * write_s64() is a signed version of write_u64() */ - int (*write_s64) (struct cgroup *cgrp, struct cftype *cft, s64 val); + int (*write_s64)(struct cgroup *cgrp, struct cftype *cft, s64 val); /* * trigger() callback can be used to get some kick from the @@ -256,7 +256,7 @@ struct cftype { */ int (*trigger)(struct cgroup *cgrp, unsigned int event); - int (*release) (struct inode *inode, struct file *file); + int (*release)(struct inode *inode, struct file *file); }; struct cgroup_scanner { -- cgit From db3b14978abc02041046ed8353f0899cb58ffffc Mon Sep 17 00:00:00 2001 From: Paul Menage Date: Fri, 25 Jul 2008 01:46:58 -0700 Subject: cgroup files: add write_string cgroup control file method This patch adds a write_string() method for cgroups control files. The semantics are that a buffer is copied from userspace to kernelspace and the handler function invoked on that buffer. The buffer is guaranteed to be nul-terminated, and no longer than max_write_len (defaulting to 64 bytes if unspecified). Later patches will convert existing raw file write handlers in control group subsystems to use this method. Signed-off-by: Paul Menage Cc: Paul Jackson Cc: Pavel Emelyanov Acked-by: Balbir Singh Acked-by: Serge Hallyn Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 14 ++++++++++++++ kernel/cgroup.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 88a734edccbc..f5379455bb59 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -205,6 +205,13 @@ struct cftype { * subsystem, followed by a period */ char name[MAX_CFTYPE_NAME]; int private; + + /* + * If non-zero, defines the maximum length of string that can + * be passed to write_string; defaults to 64 + */ + size_t max_write_len; + int (*open)(struct inode *inode, struct file *file); ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, struct file *file, @@ -248,6 +255,13 @@ struct cftype { */ int (*write_s64)(struct cgroup *cgrp, struct cftype *cft, s64 val); + /* + * write_string() is passed a nul-terminated kernelspace + * buffer of maximum length determined by max_write_len. + * Returns 0 or -ve error code. + */ + int (*write_string)(struct cgroup *cgrp, struct cftype *cft, + const char *buffer); /* * trigger() callback can be used to get some kick from the * userspace, when the actual string written is not important diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 70d083c6fb6b..3a99cc2df860 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1363,6 +1363,39 @@ static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, return retval; } +static ssize_t cgroup_write_string(struct cgroup *cgrp, struct cftype *cft, + struct file *file, + const char __user *userbuf, + size_t nbytes, loff_t *unused_ppos) +{ + char local_buffer[64]; + int retval = 0; + size_t max_bytes = cft->max_write_len; + char *buffer = local_buffer; + + if (!max_bytes) + max_bytes = sizeof(local_buffer) - 1; + if (nbytes >= max_bytes) + return -E2BIG; + /* Allocate a dynamic buffer if we need one */ + if (nbytes >= sizeof(local_buffer)) { + buffer = kmalloc(nbytes + 1, GFP_KERNEL); + if (buffer == NULL) + return -ENOMEM; + } + if (nbytes && copy_from_user(buffer, userbuf, nbytes)) + return -EFAULT; + + buffer[nbytes] = 0; /* nul-terminate */ + strstrip(buffer); + retval = cft->write_string(cgrp, cft, buffer); + if (!retval) + retval = nbytes; + if (buffer != local_buffer) + kfree(buffer); + return retval; +} + static ssize_t cgroup_common_file_write(struct cgroup *cgrp, struct cftype *cft, struct file *file, @@ -1440,6 +1473,8 @@ static ssize_t cgroup_file_write(struct file *file, const char __user *buf, return cft->write(cgrp, cft, file, buf, nbytes, ppos); if (cft->write_u64 || cft->write_s64) return cgroup_write_X64(cgrp, cft, file, buf, nbytes, ppos); + if (cft->write_string) + return cgroup_write_string(cgrp, cft, file, buf, nbytes, ppos); if (cft->trigger) { int ret = cft->trigger(cgrp, (unsigned int)cft->private); return ret ? ret : nbytes; -- cgit From e788e066c651b1bbf4a927dc95395c1aa13be436 Mon Sep 17 00:00:00 2001 From: Paul Menage Date: Fri, 25 Jul 2008 01:46:59 -0700 Subject: cgroup files: move the release_agent file to use typed handlers Adds cgroup_release_agent_write() and cgroup_release_agent_show() methods to handle writing/reading the path to a cgroup hierarchy's release agent. As a result, cgroup_common_file_read() is now unnecessary. As part of the change, a previously-tolerated race in cgroup_release_agent() is avoided by copying the current release_agent_path prior to calling call_usermode_helper(). Signed-off-by: Paul Menage Cc: Paul Jackson Cc: Pavel Emelyanov Cc: Balbir Singh Acked-by: Serge Hallyn Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 2 + kernel/cgroup.c | 125 ++++++++++++++++++++++--------------------------- 2 files changed, 59 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index f5379455bb59..e78377a91a74 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -295,6 +295,8 @@ int cgroup_add_files(struct cgroup *cgrp, int cgroup_is_removed(const struct cgroup *cgrp); +int cgroup_lock_live_group(struct cgroup *cgrp); + int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen); int cgroup_task_count(const struct cgroup *cgrp); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 3a99cc2df860..0120b5d67a73 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -89,11 +89,7 @@ struct cgroupfs_root { /* Hierarchy-specific flags */ unsigned long flags; - /* The path to use for release notifications. No locking - * between setting and use - so if userspace updates this - * while child cgroups exist, you could miss a - * notification. We ensure that it's always a valid - * NUL-terminated string */ + /* The path to use for release notifications. */ char release_agent_path[PATH_MAX]; }; @@ -1329,6 +1325,45 @@ enum cgroup_filetype { FILE_RELEASE_AGENT, }; +/** + * cgroup_lock_live_group - take cgroup_mutex and check that cgrp is alive. + * @cgrp: the cgroup to be checked for liveness + * + * Returns true (with lock held) on success, or false (with no lock + * held) on failure. + */ +int cgroup_lock_live_group(struct cgroup *cgrp) +{ + mutex_lock(&cgroup_mutex); + if (cgroup_is_removed(cgrp)) { + mutex_unlock(&cgroup_mutex); + return false; + } + return true; +} + +static int cgroup_release_agent_write(struct cgroup *cgrp, struct cftype *cft, + const char *buffer) +{ + BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); + if (!cgroup_lock_live_group(cgrp)) + return -ENODEV; + strcpy(cgrp->root->release_agent_path, buffer); + mutex_unlock(&cgroup_mutex); + return 0; +} + +static int cgroup_release_agent_show(struct cgroup *cgrp, struct cftype *cft, + struct seq_file *seq) +{ + if (!cgroup_lock_live_group(cgrp)) + return -ENODEV; + seq_puts(seq, cgrp->root->release_agent_path); + seq_putc(seq, '\n'); + mutex_unlock(&cgroup_mutex); + return 0; +} + static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, struct file *file, const char __user *userbuf, @@ -1443,10 +1478,6 @@ static ssize_t cgroup_common_file_write(struct cgroup *cgrp, else clear_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); break; - case FILE_RELEASE_AGENT: - BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); - strcpy(cgrp->root->release_agent_path, buffer); - break; default: retval = -EINVAL; goto out2; @@ -1506,49 +1537,6 @@ static ssize_t cgroup_read_s64(struct cgroup *cgrp, struct cftype *cft, return simple_read_from_buffer(buf, nbytes, ppos, tmp, len); } -static ssize_t cgroup_common_file_read(struct cgroup *cgrp, - struct cftype *cft, - struct file *file, - char __user *buf, - size_t nbytes, loff_t *ppos) -{ - enum cgroup_filetype type = cft->private; - char *page; - ssize_t retval = 0; - char *s; - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) - return -ENOMEM; - - s = page; - - switch (type) { - case FILE_RELEASE_AGENT: - { - struct cgroupfs_root *root; - size_t n; - mutex_lock(&cgroup_mutex); - root = cgrp->root; - n = strnlen(root->release_agent_path, - sizeof(root->release_agent_path)); - n = min(n, (size_t) PAGE_SIZE); - strncpy(s, root->release_agent_path, n); - mutex_unlock(&cgroup_mutex); - s += n; - break; - } - default: - retval = -EINVAL; - goto out; - } - *s++ = '\n'; - - retval = simple_read_from_buffer(buf, nbytes, ppos, page, s - page); -out: - free_page((unsigned long)page); - return retval; -} - static ssize_t cgroup_file_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { @@ -1606,6 +1594,7 @@ int cgroup_seqfile_release(struct inode *inode, struct file *file) static struct file_operations cgroup_seqfile_operations = { .read = seq_read, + .write = cgroup_file_write, .llseek = seq_lseek, .release = cgroup_seqfile_release, }; @@ -2283,8 +2272,9 @@ static struct cftype files[] = { static struct cftype cft_release_agent = { .name = "release_agent", - .read = cgroup_common_file_read, - .write = cgroup_common_file_write, + .read_seq_string = cgroup_release_agent_show, + .write_string = cgroup_release_agent_write, + .max_write_len = PATH_MAX, .private = FILE_RELEASE_AGENT, }; @@ -3111,27 +3101,24 @@ static void cgroup_release_agent(struct work_struct *work) while (!list_empty(&release_list)) { char *argv[3], *envp[3]; int i; - char *pathbuf; + char *pathbuf = NULL, *agentbuf = NULL; struct cgroup *cgrp = list_entry(release_list.next, struct cgroup, release_list); list_del_init(&cgrp->release_list); spin_unlock(&release_list_lock); pathbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!pathbuf) { - spin_lock(&release_list_lock); - continue; - } - - if (cgroup_path(cgrp, pathbuf, PAGE_SIZE) < 0) { - kfree(pathbuf); - spin_lock(&release_list_lock); - continue; - } + if (!pathbuf) + goto continue_free; + if (cgroup_path(cgrp, pathbuf, PAGE_SIZE) < 0) + goto continue_free; + agentbuf = kstrdup(cgrp->root->release_agent_path, GFP_KERNEL); + if (!agentbuf) + goto continue_free; i = 0; - argv[i++] = cgrp->root->release_agent_path; - argv[i++] = (char *)pathbuf; + argv[i++] = agentbuf; + argv[i++] = pathbuf; argv[i] = NULL; i = 0; @@ -3145,8 +3132,10 @@ static void cgroup_release_agent(struct work_struct *work) * be a slow process */ mutex_unlock(&cgroup_mutex); call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); - kfree(pathbuf); mutex_lock(&cgroup_mutex); + continue_free: + kfree(pathbuf); + kfree(agentbuf); spin_lock(&release_list_lock); } spin_unlock(&release_list_lock); -- cgit From 84eea842886ac35020be6043e04748ed22014359 Mon Sep 17 00:00:00 2001 From: Paul Menage Date: Fri, 25 Jul 2008 01:47:00 -0700 Subject: cgroups: misc cleanups to write_string patchset This patch contains cleanups suggested by reviewers for the recent write_string() patchset: - pair cgroup_lock_live_group() with cgroup_unlock() in cgroup.c for clarity, rather than directly unlocking cgroup_mutex. - make the return type of cgroup_lock_live_group() a bool - use a #define'd constant for the local buffer size in read/write functions Signed-off-by: Paul Menage Cc: Paul Jackson Cc: Pavel Emelyanov Cc: Balbir Singh Acked-by: Serge Hallyn Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 4 ++-- kernel/cgroup.c | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index e78377a91a74..cc59d3a21d87 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -21,11 +21,13 @@ struct cgroupfs_root; struct cgroup_subsys; struct inode; +struct cgroup; extern int cgroup_init_early(void); extern int cgroup_init(void); extern void cgroup_init_smp(void); extern void cgroup_lock(void); +extern bool cgroup_lock_live_group(struct cgroup *cgrp); extern void cgroup_unlock(void); extern void cgroup_fork(struct task_struct *p); extern void cgroup_fork_callbacks(struct task_struct *p); @@ -295,8 +297,6 @@ int cgroup_add_files(struct cgroup *cgrp, int cgroup_is_removed(const struct cgroup *cgrp); -int cgroup_lock_live_group(struct cgroup *cgrp); - int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen); int cgroup_task_count(const struct cgroup *cgrp); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 0120b5d67a73..a14122ecaa5e 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1329,10 +1329,10 @@ enum cgroup_filetype { * cgroup_lock_live_group - take cgroup_mutex and check that cgrp is alive. * @cgrp: the cgroup to be checked for liveness * - * Returns true (with lock held) on success, or false (with no lock - * held) on failure. + * On success, returns true; the lock should be later released with + * cgroup_unlock(). On failure returns false with no lock held. */ -int cgroup_lock_live_group(struct cgroup *cgrp) +bool cgroup_lock_live_group(struct cgroup *cgrp) { mutex_lock(&cgroup_mutex); if (cgroup_is_removed(cgrp)) { @@ -1349,7 +1349,7 @@ static int cgroup_release_agent_write(struct cgroup *cgrp, struct cftype *cft, if (!cgroup_lock_live_group(cgrp)) return -ENODEV; strcpy(cgrp->root->release_agent_path, buffer); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return 0; } @@ -1360,16 +1360,19 @@ static int cgroup_release_agent_show(struct cgroup *cgrp, struct cftype *cft, return -ENODEV; seq_puts(seq, cgrp->root->release_agent_path); seq_putc(seq, '\n'); - mutex_unlock(&cgroup_mutex); + cgroup_unlock(); return 0; } +/* A buffer size big enough for numbers or short strings */ +#define CGROUP_LOCAL_BUFFER_SIZE 64 + static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, struct file *file, const char __user *userbuf, size_t nbytes, loff_t *unused_ppos) { - char buffer[64]; + char buffer[CGROUP_LOCAL_BUFFER_SIZE]; int retval = 0; char *end; @@ -1403,7 +1406,7 @@ static ssize_t cgroup_write_string(struct cgroup *cgrp, struct cftype *cft, const char __user *userbuf, size_t nbytes, loff_t *unused_ppos) { - char local_buffer[64]; + char local_buffer[CGROUP_LOCAL_BUFFER_SIZE]; int retval = 0; size_t max_bytes = cft->max_write_len; char *buffer = local_buffer; @@ -1518,7 +1521,7 @@ static ssize_t cgroup_read_u64(struct cgroup *cgrp, struct cftype *cft, char __user *buf, size_t nbytes, loff_t *ppos) { - char tmp[64]; + char tmp[CGROUP_LOCAL_BUFFER_SIZE]; u64 val = cft->read_u64(cgrp, cft); int len = sprintf(tmp, "%llu\n", (unsigned long long) val); @@ -1530,7 +1533,7 @@ static ssize_t cgroup_read_s64(struct cgroup *cgrp, struct cftype *cft, char __user *buf, size_t nbytes, loff_t *ppos) { - char tmp[64]; + char tmp[CGROUP_LOCAL_BUFFER_SIZE]; s64 val = cft->read_s64(cgrp, cft); int len = sprintf(tmp, "%lld\n", (long long) val); -- cgit From 856c13aa1ff6136c1968414fdea5938ea9d5ebf2 Mon Sep 17 00:00:00 2001 From: Paul Menage Date: Fri, 25 Jul 2008 01:47:04 -0700 Subject: cgroup files: convert res_counter_write() to be a cgroups write_string() handler Currently res_counter_write() is a raw file handler even though it's ultimately taking a number, since in some cases it wants to pre-process the string when converting it to a number. This patch converts res_counter_write() from a raw file handler to a write_string() handler; this allows some of the boilerplate copying/locking/checking to be removed, and simplies the cleanup path, since these functions are now performed by the cgroups framework. [lizf@cn.fujitsu.com: build fix] Signed-off-by: Paul Menage Cc: Paul Jackson Cc: Pavel Emelyanov Cc: Balbir Singh Cc: Serge Hallyn Cc: KAMEZAWA Hiroyuki Signed-off-by: Li Zefan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/res_counter.h | 11 ++++++++--- kernel/res_counter.c | 48 ++++++++++++++++++++------------------------- mm/memcontrol.c | 24 +++++------------------ 3 files changed, 34 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index 125660e7793f..290205dfe094 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -63,9 +63,14 @@ u64 res_counter_read_u64(struct res_counter *counter, int member); ssize_t res_counter_read(struct res_counter *counter, int member, const char __user *buf, size_t nbytes, loff_t *pos, int (*read_strategy)(unsigned long long val, char *s)); -ssize_t res_counter_write(struct res_counter *counter, int member, - const char __user *buf, size_t nbytes, loff_t *pos, - int (*write_strategy)(char *buf, unsigned long long *val)); + +typedef int (*write_strategy_fn)(const char *buf, unsigned long long *val); + +int res_counter_memparse_write_strategy(const char *buf, + unsigned long long *res); + +int res_counter_write(struct res_counter *counter, int member, + const char *buffer, write_strategy_fn write_strategy); /* * the field descriptors. one for each member of res_counter diff --git a/kernel/res_counter.c b/kernel/res_counter.c index d3c61b4ebef2..f275c8eca772 100644 --- a/kernel/res_counter.c +++ b/kernel/res_counter.c @@ -13,6 +13,7 @@ #include #include #include +#include void res_counter_init(struct res_counter *counter) { @@ -102,44 +103,37 @@ u64 res_counter_read_u64(struct res_counter *counter, int member) return *res_counter_member(counter, member); } -ssize_t res_counter_write(struct res_counter *counter, int member, - const char __user *userbuf, size_t nbytes, loff_t *pos, - int (*write_strategy)(char *st_buf, unsigned long long *val)) +int res_counter_memparse_write_strategy(const char *buf, + unsigned long long *res) { - int ret; - char *buf, *end; - unsigned long flags; - unsigned long long tmp, *val; - - buf = kmalloc(nbytes + 1, GFP_KERNEL); - ret = -ENOMEM; - if (buf == NULL) - goto out; + char *end; + /* FIXME - make memparse() take const char* args */ + *res = memparse((char *)buf, &end); + if (*end != '\0') + return -EINVAL; - buf[nbytes] = '\0'; - ret = -EFAULT; - if (copy_from_user(buf, userbuf, nbytes)) - goto out_free; + *res = PAGE_ALIGN(*res); + return 0; +} - ret = -EINVAL; +int res_counter_write(struct res_counter *counter, int member, + const char *buf, write_strategy_fn write_strategy) +{ + char *end; + unsigned long flags; + unsigned long long tmp, *val; - strstrip(buf); if (write_strategy) { - if (write_strategy(buf, &tmp)) { - goto out_free; - } + if (write_strategy(buf, &tmp)) + return -EINVAL; } else { tmp = simple_strtoull(buf, &end, 10); if (*end != '\0') - goto out_free; + return -EINVAL; } spin_lock_irqsave(&counter->lock, flags); val = res_counter_member(counter, member); *val = tmp; spin_unlock_irqrestore(&counter->lock, flags); - ret = nbytes; -out_free: - kfree(buf); -out: - return ret; + return 0; } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index e46451e1d9b7..7385d58fb061 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -838,32 +838,18 @@ out: return ret; } -static int mem_cgroup_write_strategy(char *buf, unsigned long long *tmp) -{ - *tmp = memparse(buf, &buf); - if (*buf != '\0') - return -EINVAL; - - /* - * Round up the value to the closest page size - */ - *tmp = ((*tmp + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; - return 0; -} - static u64 mem_cgroup_read(struct cgroup *cont, struct cftype *cft) { return res_counter_read_u64(&mem_cgroup_from_cont(cont)->res, cft->private); } -static ssize_t mem_cgroup_write(struct cgroup *cont, struct cftype *cft, - struct file *file, const char __user *userbuf, - size_t nbytes, loff_t *ppos) +static int mem_cgroup_write(struct cgroup *cont, struct cftype *cft, + const char *buffer) { return res_counter_write(&mem_cgroup_from_cont(cont)->res, - cft->private, userbuf, nbytes, ppos, - mem_cgroup_write_strategy); + cft->private, buffer, + res_counter_memparse_write_strategy); } static int mem_cgroup_reset(struct cgroup *cont, unsigned int event) @@ -940,7 +926,7 @@ static struct cftype mem_cgroup_files[] = { { .name = "limit_in_bytes", .private = RES_LIMIT, - .write = mem_cgroup_write, + .write_string = mem_cgroup_write, .read_u64 = mem_cgroup_read, }, { -- cgit From e885dcde75685e09f23cffae1f6d5169c105b8a0 Mon Sep 17 00:00:00 2001 From: "Serge E. Hallyn" Date: Fri, 25 Jul 2008 01:47:06 -0700 Subject: cgroup_clone: use pid of newly created task for new cgroup cgroup_clone creates a new cgroup with the pid of the task. This works correctly for unshare, but for clone cgroup_clone is called from copy_namespaces inside copy_process, which happens before the new pid is created. As a result, the new cgroup was created with current's pid. This patch: 1. Moves the call inside copy_process to after the new pid is created 2. Passes the struct pid into ns_cgroup_clone (as it is not yet attached to the task) 3. Passes a name from ns_cgroup_clone() into cgroup_clone() so as to keep cgroup_clone() itself simpler 4. Uses pid_vnr() to get the process id value, so that the pid used to name the new cgroup is always the pid as it would be known to the task which did the cloning or unsharing. I think that is the most intuitive thing to do. This way, task t1 does clone(CLONE_NEWPID) to get t2, which does clone(CLONE_NEWPID) to get t3, then the cgroup for t3 will be named for the pid by which t2 knows t3. (Thanks to Dan Smith for finding the main bug) Changelog: June 11: Incorporate Paul Menage's feedback: don't pass NULL to ns_cgroup_clone from unshare, and reduce patch size by using 'nodename' in cgroup_clone. June 10: Original version [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Serge Hallyn Acked-by: Paul Menage Tested-by: Dan Smith Cc: Balbir Singh Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup.h | 3 ++- include/linux/nsproxy.h | 7 +++++-- kernel/cgroup.c | 7 +++---- kernel/fork.c | 6 ++++++ kernel/ns_cgroup.c | 8 ++++++-- kernel/nsproxy.c | 8 +------- 6 files changed, 23 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index cc59d3a21d87..c98dd7cb7076 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -364,7 +364,8 @@ static inline struct cgroup* task_cgroup(struct task_struct *task, return task_subsys_state(task, subsys_id)->cgroup; } -int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *ss); +int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *ss, + char *nodename); /* A cgroup_iter should be treated as an opaque object */ struct cgroup_iter { diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h index 0e66b57631fc..c8a768e59640 100644 --- a/include/linux/nsproxy.h +++ b/include/linux/nsproxy.h @@ -82,9 +82,12 @@ static inline void get_nsproxy(struct nsproxy *ns) } #ifdef CONFIG_CGROUP_NS -int ns_cgroup_clone(struct task_struct *tsk); +int ns_cgroup_clone(struct task_struct *tsk, struct pid *pid); #else -static inline int ns_cgroup_clone(struct task_struct *tsk) { return 0; } +static inline int ns_cgroup_clone(struct task_struct *tsk, struct pid *pid) +{ + return 0; +} #endif #endif diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 86b71e714e13..66ec9fd21e0c 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2848,16 +2848,17 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks) * cgroup_clone - clone the cgroup the given subsystem is attached to * @tsk: the task to be moved * @subsys: the given subsystem + * @nodename: the name for the new cgroup * * Duplicate the current cgroup in the hierarchy that the given * subsystem is attached to, and move this task into the new * child. */ -int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys) +int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys, + char *nodename) { struct dentry *dentry; int ret = 0; - char nodename[MAX_CGROUP_TYPE_NAMELEN]; struct cgroup *parent, *child; struct inode *inode; struct css_set *cg; @@ -2882,8 +2883,6 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys) cg = tsk->cgroups; parent = task_cgroup(tsk, subsys->subsys_id); - snprintf(nodename, MAX_CGROUP_TYPE_NAMELEN, "%d", tsk->pid); - /* Pin the hierarchy */ atomic_inc(&parent->root->sb->s_active); diff --git a/kernel/fork.c b/kernel/fork.c index 5a5d6fef341d..228f80c9155a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1107,6 +1107,12 @@ static struct task_struct *copy_process(unsigned long clone_flags, if (clone_flags & CLONE_THREAD) p->tgid = current->tgid; + if (current->nsproxy != p->nsproxy) { + retval = ns_cgroup_clone(p, pid); + if (retval) + goto bad_fork_free_pid; + } + p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; /* * Clear TID on mm_release()? diff --git a/kernel/ns_cgroup.c b/kernel/ns_cgroup.c index 48d7ed6fc3a4..43c2111cd54d 100644 --- a/kernel/ns_cgroup.c +++ b/kernel/ns_cgroup.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -24,9 +25,12 @@ static inline struct ns_cgroup *cgroup_to_ns( struct ns_cgroup, css); } -int ns_cgroup_clone(struct task_struct *task) +int ns_cgroup_clone(struct task_struct *task, struct pid *pid) { - return cgroup_clone(task, &ns_subsys); + char name[PROC_NUMBUF]; + + snprintf(name, PROC_NUMBUF, "%d", pid_vnr(pid)); + return cgroup_clone(task, &ns_subsys, name); } /* diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index adc785146a1c..21575fc46d05 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -157,12 +157,6 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) goto out; } - err = ns_cgroup_clone(tsk); - if (err) { - put_nsproxy(new_ns); - goto out; - } - tsk->nsproxy = new_ns; out: @@ -209,7 +203,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags, goto out; } - err = ns_cgroup_clone(current); + err = ns_cgroup_clone(current, task_pid(current)); if (err) put_nsproxy(*new_nsp); -- cgit From e8589cc189f96b87348ae83ea4db38eaac624135 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 25 Jul 2008 01:47:10 -0700 Subject: memcg: better migration handling This patch changes page migration under memory controller to use a different algorithm. (thanks to Christoph for new idea.) Before: - page_cgroup is migrated from an old page to a new page. After: - a new page is accounted , no reuse of page_cgroup. Pros: - We can avoid compliated lock depndencies and races in migration. Cons: - new param to mem_cgroup_charge_common(). - mem_cgroup_getref() is added for handling ref_cnt ping-pong. This version simplifies complicated lock dependency in page migraiton under memory resource controller. new refcnt sequence is following. a mapped page: prepage_migration() ..... +1 to NEW page try_to_unmap() ..... all refs to OLD page is gone. move_pages() ..... +1 to NEW page if page cache. remap... ..... all refs from *map* is added to NEW one. end_migration() ..... -1 to New page. page's mapcount + (page_is_cache) refs are added to NEW one. Signed-off-by: KAMEZAWA Hiroyuki Cc: Balbir Singh Cc: Pavel Emelyanov Cc: Li Zefan Cc: YAMAMOTO Takashi Cc: Hugh Dickins Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 11 ++-- mm/memcontrol.c | 128 +++++++++++++++++++++++---------------------- mm/migrate.c | 22 +++++--- 3 files changed, 86 insertions(+), 75 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index e6608776bc96..84ead2aa6f18 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -50,9 +50,10 @@ extern struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p); #define mm_match_cgroup(mm, cgroup) \ ((cgroup) == mem_cgroup_from_task((mm)->owner)) -extern int mem_cgroup_prepare_migration(struct page *page); +extern int +mem_cgroup_prepare_migration(struct page *page, struct page *newpage); extern void mem_cgroup_end_migration(struct page *page); -extern void mem_cgroup_page_migration(struct page *page, struct page *newpage); +extern int mem_cgroup_getref(struct page *page); /* * For memory reclaim. @@ -112,7 +113,8 @@ static inline int task_in_mem_cgroup(struct task_struct *task, return 1; } -static inline int mem_cgroup_prepare_migration(struct page *page) +static inline int +mem_cgroup_prepare_migration(struct page *page, struct page *newpage) { return 0; } @@ -121,8 +123,7 @@ static inline void mem_cgroup_end_migration(struct page *page) { } -static inline void -mem_cgroup_page_migration(struct page *page, struct page *newpage) +static inline void mem_cgroup_getref(struct page *page) { } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 90ccc1326356..da5912b84551 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -524,7 +524,8 @@ unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, * < 0 if the cgroup is over its limit */ static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, - gfp_t gfp_mask, enum charge_type ctype) + gfp_t gfp_mask, enum charge_type ctype, + struct mem_cgroup *memcg) { struct mem_cgroup *mem; struct page_cgroup *pc; @@ -569,16 +570,21 @@ retry: * thread group leader migrates. It's possible that mm is not * set, if so charge the init_mm (happens for pagecache usage). */ - if (!mm) - mm = &init_mm; + if (!memcg) { + if (!mm) + mm = &init_mm; - rcu_read_lock(); - mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); - /* - * For every charge from the cgroup, increment reference count - */ - css_get(&mem->css); - rcu_read_unlock(); + rcu_read_lock(); + mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); + /* + * For every charge from the cgroup, increment reference count + */ + css_get(&mem->css); + rcu_read_unlock(); + } else { + mem = memcg; + css_get(&memcg->css); + } while (res_counter_charge(&mem->res, PAGE_SIZE)) { if (!(gfp_mask & __GFP_WAIT)) @@ -648,7 +654,7 @@ err: int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) { return mem_cgroup_charge_common(page, mm, gfp_mask, - MEM_CGROUP_CHARGE_TYPE_MAPPED); + MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); } int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, @@ -657,7 +663,22 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, if (!mm) mm = &init_mm; return mem_cgroup_charge_common(page, mm, gfp_mask, - MEM_CGROUP_CHARGE_TYPE_CACHE); + MEM_CGROUP_CHARGE_TYPE_CACHE, NULL); +} + +int mem_cgroup_getref(struct page *page) +{ + struct page_cgroup *pc; + + if (mem_cgroup_subsys.disabled) + return 0; + + lock_page_cgroup(page); + pc = page_get_page_cgroup(page); + VM_BUG_ON(!pc); + pc->ref_cnt++; + unlock_page_cgroup(page); + return 0; } /* @@ -707,65 +728,39 @@ unlock: } /* - * Returns non-zero if a page (under migration) has valid page_cgroup member. - * Refcnt of page_cgroup is incremented. + * Before starting migration, account against new page. */ -int mem_cgroup_prepare_migration(struct page *page) +int mem_cgroup_prepare_migration(struct page *page, struct page *newpage) { struct page_cgroup *pc; + struct mem_cgroup *mem = NULL; + enum charge_type ctype = MEM_CGROUP_CHARGE_TYPE_MAPPED; + int ret = 0; if (mem_cgroup_subsys.disabled) return 0; lock_page_cgroup(page); pc = page_get_page_cgroup(page); - if (pc) - pc->ref_cnt++; + if (pc) { + mem = pc->mem_cgroup; + css_get(&mem->css); + if (pc->flags & PAGE_CGROUP_FLAG_CACHE) + ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; + } unlock_page_cgroup(page); - return pc != NULL; -} - -void mem_cgroup_end_migration(struct page *page) -{ - mem_cgroup_uncharge_page(page); + if (mem) { + ret = mem_cgroup_charge_common(newpage, NULL, GFP_KERNEL, + ctype, mem); + css_put(&mem->css); + } + return ret; } -/* - * We know both *page* and *newpage* are now not-on-LRU and PG_locked. - * And no race with uncharge() routines because page_cgroup for *page* - * has extra one reference by mem_cgroup_prepare_migration. - */ -void mem_cgroup_page_migration(struct page *page, struct page *newpage) +/* remove redundant charge */ +void mem_cgroup_end_migration(struct page *newpage) { - struct page_cgroup *pc; - struct mem_cgroup_per_zone *mz; - unsigned long flags; - - lock_page_cgroup(page); - pc = page_get_page_cgroup(page); - if (!pc) { - unlock_page_cgroup(page); - return; - } - - mz = page_cgroup_zoneinfo(pc); - spin_lock_irqsave(&mz->lru_lock, flags); - __mem_cgroup_remove_list(mz, pc); - spin_unlock_irqrestore(&mz->lru_lock, flags); - - page_assign_page_cgroup(page, NULL); - unlock_page_cgroup(page); - - pc->page = newpage; - lock_page_cgroup(newpage); - page_assign_page_cgroup(newpage, pc); - - mz = page_cgroup_zoneinfo(pc); - spin_lock_irqsave(&mz->lru_lock, flags); - __mem_cgroup_add_list(mz, pc); - spin_unlock_irqrestore(&mz->lru_lock, flags); - - unlock_page_cgroup(newpage); + mem_cgroup_uncharge_page(newpage); } /* @@ -795,12 +790,19 @@ static void mem_cgroup_force_empty_list(struct mem_cgroup *mem, page = pc->page; get_page(page); spin_unlock_irqrestore(&mz->lru_lock, flags); - mem_cgroup_uncharge_page(page); - put_page(page); - if (--count <= 0) { - count = FORCE_UNCHARGE_BATCH; + /* + * Check if this page is on LRU. !LRU page can be found + * if it's under page migration. + */ + if (PageLRU(page)) { + mem_cgroup_uncharge_page(page); + put_page(page); + if (--count <= 0) { + count = FORCE_UNCHARGE_BATCH; + cond_resched(); + } + } else cond_resched(); - } spin_lock_irqsave(&mz->lru_lock, flags); } spin_unlock_irqrestore(&mz->lru_lock, flags); diff --git a/mm/migrate.c b/mm/migrate.c index 376cceba82f9..f6d7f8efd1a8 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -358,6 +358,10 @@ static int migrate_page_move_mapping(struct address_space *mapping, __inc_zone_page_state(newpage, NR_FILE_PAGES); write_unlock_irq(&mapping->tree_lock); + if (!PageSwapCache(newpage)) { + mem_cgroup_uncharge_page(page); + mem_cgroup_getref(newpage); + } return 0; } @@ -611,7 +615,6 @@ static int move_to_new_page(struct page *newpage, struct page *page) rc = fallback_migrate_page(mapping, newpage, page); if (!rc) { - mem_cgroup_page_migration(page, newpage); remove_migration_ptes(page, newpage); } else newpage->mapping = NULL; @@ -641,6 +644,14 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, /* page was freed from under us. So we are done. */ goto move_newpage; + charge = mem_cgroup_prepare_migration(page, newpage); + if (charge == -ENOMEM) { + rc = -ENOMEM; + goto move_newpage; + } + /* prepare cgroup just returns 0 or -ENOMEM */ + BUG_ON(charge); + rc = -EAGAIN; if (TestSetPageLocked(page)) { if (!force) @@ -692,19 +703,14 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, goto rcu_unlock; } - charge = mem_cgroup_prepare_migration(page); /* Establish migration ptes or remove ptes */ try_to_unmap(page, 1); if (!page_mapped(page)) rc = move_to_new_page(newpage, page); - if (rc) { + if (rc) remove_migration_ptes(page, page); - if (charge) - mem_cgroup_end_migration(page); - } else if (charge) - mem_cgroup_end_migration(newpage); rcu_unlock: if (rcu_locked) rcu_read_unlock(); @@ -725,6 +731,8 @@ unlock: } move_newpage: + if (!charge) + mem_cgroup_end_migration(newpage); /* * Move the new page to the LRU. If migration was not successful * then this will free the page. -- cgit From 69029cd550284e32de13d6dd2f77b723c8a0e444 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 25 Jul 2008 01:47:14 -0700 Subject: memcg: remove refcnt from page_cgroup memcg: performance improvements Patch Description 1/5 ... remove refcnt fron page_cgroup patch (shmem handling is fixed) 2/5 ... swapcache handling patch 3/5 ... add helper function for shmem's memory reclaim patch 4/5 ... optimize by likely/unlikely ppatch 5/5 ... remove redundunt check patch (shmem handling is fixed.) Unix bench result. == 2.6.26-rc2-mm1 + memory resource controller Execl Throughput 2915.4 lps (29.6 secs, 3 samples) C Compiler Throughput 1019.3 lpm (60.0 secs, 3 samples) Shell Scripts (1 concurrent) 5796.0 lpm (60.0 secs, 3 samples) Shell Scripts (8 concurrent) 1097.7 lpm (60.0 secs, 3 samples) Shell Scripts (16 concurrent) 565.3 lpm (60.0 secs, 3 samples) File Read 1024 bufsize 2000 maxblocks 1022128.0 KBps (30.0 secs, 3 samples) File Write 1024 bufsize 2000 maxblocks 544057.0 KBps (30.0 secs, 3 samples) File Copy 1024 bufsize 2000 maxblocks 346481.0 KBps (30.0 secs, 3 samples) File Read 256 bufsize 500 maxblocks 319325.0 KBps (30.0 secs, 3 samples) File Write 256 bufsize 500 maxblocks 148788.0 KBps (30.0 secs, 3 samples) File Copy 256 bufsize 500 maxblocks 99051.0 KBps (30.0 secs, 3 samples) File Read 4096 bufsize 8000 maxblocks 2058917.0 KBps (30.0 secs, 3 samples) File Write 4096 bufsize 8000 maxblocks 1606109.0 KBps (30.0 secs, 3 samples) File Copy 4096 bufsize 8000 maxblocks 854789.0 KBps (30.0 secs, 3 samples) Dc: sqrt(2) to 99 decimal places 126145.2 lpm (30.0 secs, 3 samples) INDEX VALUES TEST BASELINE RESULT INDEX Execl Throughput 43.0 2915.4 678.0 File Copy 1024 bufsize 2000 maxblocks 3960.0 346481.0 875.0 File Copy 256 bufsize 500 maxblocks 1655.0 99051.0 598.5 File Copy 4096 bufsize 8000 maxblocks 5800.0 854789.0 1473.8 Shell Scripts (8 concurrent) 6.0 1097.7 1829.5 ========= FINAL SCORE 991.3 == 2.6.26-rc2-mm1 + this set == Execl Throughput 3012.9 lps (29.9 secs, 3 samples) C Compiler Throughput 981.0 lpm (60.0 secs, 3 samples) Shell Scripts (1 concurrent) 5872.0 lpm (60.0 secs, 3 samples) Shell Scripts (8 concurrent) 1120.3 lpm (60.0 secs, 3 samples) Shell Scripts (16 concurrent) 578.0 lpm (60.0 secs, 3 samples) File Read 1024 bufsize 2000 maxblocks 1003993.0 KBps (30.0 secs, 3 samples) File Write 1024 bufsize 2000 maxblocks 550452.0 KBps (30.0 secs, 3 samples) File Copy 1024 bufsize 2000 maxblocks 347159.0 KBps (30.0 secs, 3 samples) File Read 256 bufsize 500 maxblocks 314644.0 KBps (30.0 secs, 3 samples) File Write 256 bufsize 500 maxblocks 151852.0 KBps (30.0 secs, 3 samples) File Copy 256 bufsize 500 maxblocks 101000.0 KBps (30.0 secs, 3 samples) File Read 4096 bufsize 8000 maxblocks 2033256.0 KBps (30.0 secs, 3 samples) File Write 4096 bufsize 8000 maxblocks 1611814.0 KBps (30.0 secs, 3 samples) File Copy 4096 bufsize 8000 maxblocks 847979.0 KBps (30.0 secs, 3 samples) Dc: sqrt(2) to 99 decimal places 128148.7 lpm (30.0 secs, 3 samples) INDEX VALUES TEST BASELINE RESULT INDEX Execl Throughput 43.0 3012.9 700.7 File Copy 1024 bufsize 2000 maxblocks 3960.0 347159.0 876.7 File Copy 256 bufsize 500 maxblocks 1655.0 101000.0 610.3 File Copy 4096 bufsize 8000 maxblocks 5800.0 847979.0 1462.0 Shell Scripts (8 concurrent) 6.0 1120.3 1867.2 ========= FINAL SCORE 1004.6 This patch: Remove refcnt from page_cgroup(). After this, * A page is charged only when !page_mapped() && no page_cgroup is assigned. * Anon page is newly mapped. * File page is added to mapping->tree. * A page is uncharged only when * Anon page is fully unmapped. * File page is removed from LRU. There is no change in behavior from user's view. This patch also removes unnecessary calls in rmap.c which was used only for refcnt mangement. [akpm@linux-foundation.org: fix warning] [hugh@veritas.com: fix shmem_unuse_inode charging] Signed-off-by: KAMEZAWA Hiroyuki Cc: Balbir Singh Cc: "Eric W. Biederman" Cc: Pavel Emelyanov Cc: Li Zefan Cc: Hugh Dickins Cc: YAMAMOTO Takashi Cc: Paul Menage Cc: David Rientjes Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 10 ++--- mm/filemap.c | 6 +-- mm/memcontrol.c | 109 ++++++++++++++++++++++++++------------------- mm/migrate.c | 3 +- mm/rmap.c | 14 +----- mm/shmem.c | 35 ++++++++++----- 6 files changed, 97 insertions(+), 80 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 84ead2aa6f18..b4980b8f048e 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -35,6 +35,7 @@ extern int mem_cgroup_charge(struct page *page, struct mm_struct *mm, extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask); extern void mem_cgroup_uncharge_page(struct page *page); +extern void mem_cgroup_uncharge_cache_page(struct page *page); extern void mem_cgroup_move_lists(struct page *page, bool active); extern unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, struct list_head *dst, @@ -53,7 +54,6 @@ extern struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p); extern int mem_cgroup_prepare_migration(struct page *page, struct page *newpage); extern void mem_cgroup_end_migration(struct page *page); -extern int mem_cgroup_getref(struct page *page); /* * For memory reclaim. @@ -98,6 +98,10 @@ static inline void mem_cgroup_uncharge_page(struct page *page) { } +static inline void mem_cgroup_uncharge_cache_page(struct page *page) +{ +} + static inline void mem_cgroup_move_lists(struct page *page, bool active) { } @@ -123,10 +127,6 @@ static inline void mem_cgroup_end_migration(struct page *page) { } -static inline void mem_cgroup_getref(struct page *page) -{ -} - static inline int mem_cgroup_calc_mapped_ratio(struct mem_cgroup *mem) { return 0; diff --git a/mm/filemap.c b/mm/filemap.c index 5d4c880d7cd9..2d3ec1ffc66e 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -115,7 +115,7 @@ void __remove_from_page_cache(struct page *page) { struct address_space *mapping = page->mapping; - mem_cgroup_uncharge_page(page); + mem_cgroup_uncharge_cache_page(page); radix_tree_delete(&mapping->page_tree, page->index); page->mapping = NULL; mapping->nrpages--; @@ -474,12 +474,12 @@ int add_to_page_cache(struct page *page, struct address_space *mapping, mapping->nrpages++; __inc_zone_page_state(page, NR_FILE_PAGES); } else - mem_cgroup_uncharge_page(page); + mem_cgroup_uncharge_cache_page(page); write_unlock_irq(&mapping->tree_lock); radix_tree_preload_end(); } else - mem_cgroup_uncharge_page(page); + mem_cgroup_uncharge_cache_page(page); out: return error; } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index da5912b84551..a61706193c31 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -166,7 +166,6 @@ struct page_cgroup { struct list_head lru; /* per cgroup LRU list */ struct page *page; struct mem_cgroup *mem_cgroup; - int ref_cnt; /* cached, mapped, migrating */ int flags; }; #define PAGE_CGROUP_FLAG_CACHE (0x1) /* charged as cache */ @@ -185,6 +184,7 @@ static enum zone_type page_cgroup_zid(struct page_cgroup *pc) enum charge_type { MEM_CGROUP_CHARGE_TYPE_CACHE = 0, MEM_CGROUP_CHARGE_TYPE_MAPPED, + MEM_CGROUP_CHARGE_TYPE_FORCE, /* used by force_empty */ }; /* @@ -552,9 +552,7 @@ retry: */ if (pc) { VM_BUG_ON(pc->page != page); - VM_BUG_ON(pc->ref_cnt <= 0); - - pc->ref_cnt++; + VM_BUG_ON(!pc->mem_cgroup); unlock_page_cgroup(page); goto done; } @@ -570,10 +568,7 @@ retry: * thread group leader migrates. It's possible that mm is not * set, if so charge the init_mm (happens for pagecache usage). */ - if (!memcg) { - if (!mm) - mm = &init_mm; - + if (likely(!memcg)) { rcu_read_lock(); mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); /* @@ -609,7 +604,6 @@ retry: } } - pc->ref_cnt = 1; pc->mem_cgroup = mem; pc->page = page; /* @@ -653,6 +647,17 @@ err: int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) { + /* + * If already mapped, we don't have to account. + * If page cache, page->mapping has address_space. + * But page->mapping may have out-of-use anon_vma pointer, + * detecit it by PageAnon() check. newly-mapped-anon's page->mapping + * is NULL. + */ + if (page_mapped(page) || (page->mapping && !PageAnon(page))) + return 0; + if (unlikely(!mm)) + mm = &init_mm; return mem_cgroup_charge_common(page, mm, gfp_mask, MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); } @@ -660,32 +665,17 @@ int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) { - if (!mm) + if (unlikely(!mm)) mm = &init_mm; return mem_cgroup_charge_common(page, mm, gfp_mask, MEM_CGROUP_CHARGE_TYPE_CACHE, NULL); } -int mem_cgroup_getref(struct page *page) -{ - struct page_cgroup *pc; - - if (mem_cgroup_subsys.disabled) - return 0; - - lock_page_cgroup(page); - pc = page_get_page_cgroup(page); - VM_BUG_ON(!pc); - pc->ref_cnt++; - unlock_page_cgroup(page); - return 0; -} - /* - * Uncharging is always a welcome operation, we never complain, simply - * uncharge. + * uncharge if !page_mapped(page) */ -void mem_cgroup_uncharge_page(struct page *page) +static void +__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) { struct page_cgroup *pc; struct mem_cgroup *mem; @@ -704,29 +694,41 @@ void mem_cgroup_uncharge_page(struct page *page) goto unlock; VM_BUG_ON(pc->page != page); - VM_BUG_ON(pc->ref_cnt <= 0); - if (--(pc->ref_cnt) == 0) { - mz = page_cgroup_zoneinfo(pc); - spin_lock_irqsave(&mz->lru_lock, flags); - __mem_cgroup_remove_list(mz, pc); - spin_unlock_irqrestore(&mz->lru_lock, flags); + if ((ctype == MEM_CGROUP_CHARGE_TYPE_MAPPED) + && ((pc->flags & PAGE_CGROUP_FLAG_CACHE) + || page_mapped(page))) + goto unlock; - page_assign_page_cgroup(page, NULL); - unlock_page_cgroup(page); + mz = page_cgroup_zoneinfo(pc); + spin_lock_irqsave(&mz->lru_lock, flags); + __mem_cgroup_remove_list(mz, pc); + spin_unlock_irqrestore(&mz->lru_lock, flags); - mem = pc->mem_cgroup; - res_counter_uncharge(&mem->res, PAGE_SIZE); - css_put(&mem->css); + page_assign_page_cgroup(page, NULL); + unlock_page_cgroup(page); - kmem_cache_free(page_cgroup_cache, pc); - return; - } + mem = pc->mem_cgroup; + res_counter_uncharge(&mem->res, PAGE_SIZE); + css_put(&mem->css); + kmem_cache_free(page_cgroup_cache, pc); + return; unlock: unlock_page_cgroup(page); } +void mem_cgroup_uncharge_page(struct page *page) +{ + __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_MAPPED); +} + +void mem_cgroup_uncharge_cache_page(struct page *page) +{ + VM_BUG_ON(page_mapped(page)); + __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE); +} + /* * Before starting migration, account against new page. */ @@ -757,15 +759,29 @@ int mem_cgroup_prepare_migration(struct page *page, struct page *newpage) return ret; } -/* remove redundant charge */ +/* remove redundant charge if migration failed*/ void mem_cgroup_end_migration(struct page *newpage) { - mem_cgroup_uncharge_page(newpage); + /* + * At success, page->mapping is not NULL. + * special rollback care is necessary when + * 1. at migration failure. (newpage->mapping is cleared in this case) + * 2. the newpage was moved but not remapped again because the task + * exits and the newpage is obsolete. In this case, the new page + * may be a swapcache. So, we just call mem_cgroup_uncharge_page() + * always for avoiding mess. The page_cgroup will be removed if + * unnecessary. File cache pages is still on radix-tree. Don't + * care it. + */ + if (!newpage->mapping) + __mem_cgroup_uncharge_common(newpage, + MEM_CGROUP_CHARGE_TYPE_FORCE); + else if (PageAnon(newpage)) + mem_cgroup_uncharge_page(newpage); } /* * This routine traverse page_cgroup in given list and drop them all. - * This routine ignores page_cgroup->ref_cnt. * *And* this routine doesn't reclaim page itself, just removes page_cgroup. */ #define FORCE_UNCHARGE_BATCH (128) @@ -795,7 +811,8 @@ static void mem_cgroup_force_empty_list(struct mem_cgroup *mem, * if it's under page migration. */ if (PageLRU(page)) { - mem_cgroup_uncharge_page(page); + __mem_cgroup_uncharge_common(page, + MEM_CGROUP_CHARGE_TYPE_FORCE); put_page(page); if (--count <= 0) { count = FORCE_UNCHARGE_BATCH; diff --git a/mm/migrate.c b/mm/migrate.c index f6d7f8efd1a8..d8c65a65c61d 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -359,8 +359,7 @@ static int migrate_page_move_mapping(struct address_space *mapping, write_unlock_irq(&mapping->tree_lock); if (!PageSwapCache(newpage)) { - mem_cgroup_uncharge_page(page); - mem_cgroup_getref(newpage); + mem_cgroup_uncharge_cache_page(page); } return 0; diff --git a/mm/rmap.c b/mm/rmap.c index bf0a5b7cfb8e..abbd29f7c43f 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -576,14 +576,8 @@ void page_add_anon_rmap(struct page *page, VM_BUG_ON(address < vma->vm_start || address >= vma->vm_end); if (atomic_inc_and_test(&page->_mapcount)) __page_set_anon_rmap(page, vma, address); - else { + else __page_check_anon_rmap(page, vma, address); - /* - * We unconditionally charged during prepare, we uncharge here - * This takes care of balancing the reference counts - */ - mem_cgroup_uncharge_page(page); - } } /** @@ -614,12 +608,6 @@ void page_add_file_rmap(struct page *page) { if (atomic_inc_and_test(&page->_mapcount)) __inc_zone_page_state(page, NR_FILE_MAPPED); - else - /* - * We unconditionally charged during prepare, we uncharge here - * This takes care of balancing the reference counts - */ - mem_cgroup_uncharge_page(page); } #ifdef CONFIG_DEBUG_VM diff --git a/mm/shmem.c b/mm/shmem.c index 9ffbea9b79e1..d58305e8a484 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -922,20 +922,26 @@ found: error = 1; if (!inode) goto out; - /* Precharge page while we can wait, compensate afterwards */ + /* Precharge page using GFP_KERNEL while we can wait */ error = mem_cgroup_cache_charge(page, current->mm, GFP_KERNEL); if (error) goto out; error = radix_tree_preload(GFP_KERNEL); - if (error) - goto uncharge; + if (error) { + mem_cgroup_uncharge_cache_page(page); + goto out; + } error = 1; spin_lock(&info->lock); ptr = shmem_swp_entry(info, idx, NULL); - if (ptr && ptr->val == entry.val) + if (ptr && ptr->val == entry.val) { error = add_to_page_cache(page, inode->i_mapping, idx, GFP_NOWAIT); + /* does mem_cgroup_uncharge_cache_page on error */ + } else /* we must compensate for our precharge above */ + mem_cgroup_uncharge_cache_page(page); + if (error == -EEXIST) { struct page *filepage = find_get_page(inode->i_mapping, idx); error = 1; @@ -961,8 +967,6 @@ found: shmem_swp_unmap(ptr); spin_unlock(&info->lock); radix_tree_preload_end(); -uncharge: - mem_cgroup_uncharge_page(page); out: unlock_page(page); page_cache_release(page); @@ -1319,7 +1323,7 @@ repeat: page_cache_release(swappage); goto failed; } - mem_cgroup_uncharge_page(swappage); + mem_cgroup_uncharge_cache_page(swappage); } page_cache_release(swappage); goto repeat; @@ -1358,6 +1362,8 @@ repeat: } if (!filepage) { + int ret; + spin_unlock(&info->lock); filepage = shmem_alloc_page(gfp, info, idx); if (!filepage) { @@ -1386,10 +1392,18 @@ repeat: swap = *entry; shmem_swp_unmap(entry); } - if (error || swap.val || 0 != add_to_page_cache_lru( - filepage, mapping, idx, GFP_NOWAIT)) { + ret = error || swap.val; + if (ret) + mem_cgroup_uncharge_cache_page(filepage); + else + ret = add_to_page_cache_lru(filepage, mapping, + idx, GFP_NOWAIT); + /* + * At add_to_page_cache_lru() failure, uncharge will + * be done automatically. + */ + if (ret) { spin_unlock(&info->lock); - mem_cgroup_uncharge_page(filepage); page_cache_release(filepage); shmem_unacct_blocks(info->flags, 1); shmem_free_blocks(inode, 1); @@ -1398,7 +1412,6 @@ repeat: goto failed; goto repeat; } - mem_cgroup_uncharge_page(filepage); info->flags |= SHMEM_PAGEIN; } -- cgit From c9b0ed51483cc2fc42bb801b6675c4231b0e4634 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 25 Jul 2008 01:47:15 -0700 Subject: memcg: helper function for relcaim from shmem. A new call, mem_cgroup_shrink_usage() is added for shmem handling and relacing non-standard usage of mem_cgroup_charge/uncharge. Now, shmem calls mem_cgroup_charge() just for reclaim some pages from mem_cgroup. In general, shmem is used by some process group and not for global resource (like file caches). So, it's reasonable to reclaim pages from mem_cgroup where shmem is mainly used. [hugh@veritas.com: shmem_getpage release page sooner] [hugh@veritas.com: mem_cgroup_shrink_usage css_put] Signed-off-by: KAMEZAWA Hiroyuki Cc: Balbir Singh Cc: "Eric W. Biederman" Cc: Pavel Emelyanov Cc: Li Zefan Cc: YAMAMOTO Takashi Cc: Paul Menage Cc: David Rientjes Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 7 +++++++ mm/memcontrol.c | 26 ++++++++++++++++++++++++++ mm/shmem.c | 11 ++++------- 3 files changed, 37 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index b4980b8f048e..fdf3967e1397 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -37,6 +37,8 @@ extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, extern void mem_cgroup_uncharge_page(struct page *page); extern void mem_cgroup_uncharge_cache_page(struct page *page); extern void mem_cgroup_move_lists(struct page *page, bool active); +extern int mem_cgroup_shrink_usage(struct mm_struct *mm, gfp_t gfp_mask); + extern unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, struct list_head *dst, unsigned long *scanned, int order, @@ -102,6 +104,11 @@ static inline void mem_cgroup_uncharge_cache_page(struct page *page) { } +static inline int mem_cgroup_shrink_usage(struct mm_struct *mm, gfp_t gfp_mask) +{ + return 0; +} + static inline void mem_cgroup_move_lists(struct page *page, bool active) { } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index a61706193c31..f46b8615de6c 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -780,6 +780,32 @@ void mem_cgroup_end_migration(struct page *newpage) mem_cgroup_uncharge_page(newpage); } +/* + * A call to try to shrink memory usage under specified resource controller. + * This is typically used for page reclaiming for shmem for reducing side + * effect of page allocation from shmem, which is used by some mem_cgroup. + */ +int mem_cgroup_shrink_usage(struct mm_struct *mm, gfp_t gfp_mask) +{ + struct mem_cgroup *mem; + int progress = 0; + int retry = MEM_CGROUP_RECLAIM_RETRIES; + + rcu_read_lock(); + mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); + css_get(&mem->css); + rcu_read_unlock(); + + do { + progress = try_to_free_mem_cgroup_pages(mem, gfp_mask); + } while (!progress && --retry); + + css_put(&mem->css); + if (!retry) + return -ENOMEM; + return 0; +} + /* * This routine traverse page_cgroup in given list and drop them all. * *And* this routine doesn't reclaim page itself, just removes page_cgroup. diff --git a/mm/shmem.c b/mm/shmem.c index d58305e8a484..f92fea94d037 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1315,17 +1315,14 @@ repeat: shmem_swp_unmap(entry); spin_unlock(&info->lock); unlock_page(swappage); + page_cache_release(swappage); if (error == -ENOMEM) { /* allow reclaim from this memory cgroup */ - error = mem_cgroup_cache_charge(swappage, - current->mm, gfp & ~__GFP_HIGHMEM); - if (error) { - page_cache_release(swappage); + error = mem_cgroup_shrink_usage(current->mm, + gfp); + if (error) goto failed; - } - mem_cgroup_uncharge_cache_page(swappage); } - page_cache_release(swappage); goto repeat; } } else if (sgp == SGP_READ && !filepage) { -- cgit From 12b9804419cfb1c1bdac413f6c373af3b88d154b Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Fri, 25 Jul 2008 01:47:19 -0700 Subject: res_counter: limit change support ebusy Add an interface to set limit. This is necessary to memory resource controller because it shrinks usage at set limit. Other controllers may not need this interface to shrink usage because shrinking is not necessary or impossible. Acked-by: Balbir Singh Acked-by: Pavel Emelyanov Signed-off-by: KAMEZAWA Hiroyuki Cc: Paul Menage Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/res_counter.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include/linux') diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index 290205dfe094..fdeadd9740dc 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -158,4 +158,20 @@ static inline void res_counter_reset_failcnt(struct res_counter *cnt) cnt->failcnt = 0; spin_unlock_irqrestore(&cnt->lock, flags); } + +static inline int res_counter_set_limit(struct res_counter *cnt, + unsigned long long limit) +{ + unsigned long flags; + int ret = -EBUSY; + + spin_lock_irqsave(&cnt->lock, flags); + if (cnt->usage < limit) { + cnt->limit = limit; + ret = 0; + } + spin_unlock_irqrestore(&cnt->lock, flags); + return ret; +} + #endif -- cgit From 364d3c13c17f45da6d638011078d4c4d3070d719 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:36 -0700 Subject: ptrace: give more respect to SIGKILL ptrace_stop() has some complicated checks to prevent the scheduling in the TASK_TRACED state with the pending SIGKILL, but these checks are racy, and they depend on arch_ptrace_stop_needed(). This patch assumes that the traced task should die asap if it was killed by SIGKILL, in that case schedule()->signal_pending_state() has no reason to ignore the TASK_WAKEKILL part of TASK_TRACED, and we can kill this nasty special case. Note: do_exit()->ptrace_notify() is special, the killed task can already dequeue SIGKILL at this point. Another indication that fatal_signal_pending() is not exactly right. Signed-off-by: Oleg Nesterov Cc: Ingo Molnar Cc: Matthew Wilcox Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 6aca4a16e377..79e749dbf81e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2054,9 +2054,6 @@ static inline int signal_pending_state(long state, struct task_struct *p) if (!signal_pending(p)) return 0; - if (state & (__TASK_STOPPED | __TASK_TRACED)) - return 0; - return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p); } -- cgit From 7b34e4283c685f5cc6ba6d30e939906eee0d4bcf Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:37 -0700 Subject: introduce PF_KTHREAD flag Introduce the new PF_KTHREAD flag to mark the kernel threads. It is set by INIT_TASK() and copied to the forked childs (we could set it in kthreadd() along with PF_NOFREEZE instead). daemonize() was changed as well. In that case testing of PF_KTHREAD is racy, but daemonize() is hopeless anyway. This flag is cleared in do_execve(), before search_binary_handler(). Probably not the best place, we can do this in exec_mmap() or in start_thread(), or clear it along with PF_FORKNOEXEC. But I think this doesn't matter in practice, and if do_execve() fails kthread should die soon. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 1 + include/linux/init_task.h | 2 +- include/linux/sched.h | 1 + kernel/exit.c | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index af249af4ccab..cd2e8c9b1249 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1326,6 +1326,7 @@ int do_execve(char * filename, if (retval < 0) goto out; + current->flags &= ~PF_KTHREAD; retval = search_binary_handler(bprm,regs); if (retval >= 0) { /* execve success */ diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 93c45acf249a..021d8e720c79 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -122,7 +122,7 @@ extern struct group_info init_groups; .state = 0, \ .stack = &init_thread_info, \ .usage = ATOMIC_INIT(2), \ - .flags = 0, \ + .flags = PF_KTHREAD, \ .lock_depth = -1, \ .prio = MAX_PRIO-20, \ .static_prio = MAX_PRIO-20, \ diff --git a/include/linux/sched.h b/include/linux/sched.h index 79e749dbf81e..eec64a4adb9d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1483,6 +1483,7 @@ static inline void put_task_struct(struct task_struct *t) #define PF_EXITING 0x00000004 /* getting shut down */ #define PF_EXITPIDONE 0x00000008 /* pi exit done on shut down */ #define PF_VCPU 0x00000010 /* I'm a virtual CPU */ +#define PF_KTHREAD 0x00000020 /* I am a kernel thread */ #define PF_FORKNOEXEC 0x00000040 /* forked but didn't exec */ #define PF_SUPERPRIV 0x00000100 /* used super-user privileges */ #define PF_DUMPCORE 0x00000200 /* dumped core */ diff --git a/kernel/exit.c b/kernel/exit.c index a7799d8a6404..28a44a2612dc 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -430,7 +430,7 @@ void daemonize(const char *name, ...) * We don't want to have TIF_FREEZE set if the system-wide hibernation * or suspend transition begins right now. */ - current->flags |= PF_NOFREEZE; + current->flags |= (PF_NOFREEZE | PF_KTHREAD); if (current->nsproxy != &init_nsproxy) { get_nsproxy(&init_nsproxy); -- cgit From 246bb0b1deb29726990620d8b5e55ca29f331362 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:38 -0700 Subject: kill PF_BORROWED_MM in favour of PF_KTHREAD Kill PF_BORROWED_MM. Change use_mm/unuse_mm to not play with ->flags, and do s/PF_BORROWED_MM/PF_KTHREAD/ for a couple of other users. No functional changes yet. But this allows us to do further fixes/cleanups. oom_kill/ptrace/etc often check "p->mm != NULL" to filter out the kthreads, this is wrong because of use_mm(). The problem with PF_BORROWED_MM is that we need task_lock() to avoid races. With this patch we can check PF_KTHREAD directly, or use a simple lockless helper: /* The result must not be dereferenced !!! */ struct mm_struct *__get_task_mm(struct task_struct *tsk) { if (tsk->flags & PF_KTHREAD) return NULL; return tsk->mm; } Note also ecard_task(). It runs with ->mm != NULL, but it's the kernel thread without PF_BORROWED_MM. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/aio.c | 2 -- include/linux/sched.h | 3 +-- kernel/fork.c | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/aio.c b/fs/aio.c index 0fb3117ddd93..0051fd94b44e 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -586,7 +586,6 @@ static void use_mm(struct mm_struct *mm) struct task_struct *tsk = current; task_lock(tsk); - tsk->flags |= PF_BORROWED_MM; active_mm = tsk->active_mm; atomic_inc(&mm->mm_count); tsk->mm = mm; @@ -610,7 +609,6 @@ static void unuse_mm(struct mm_struct *mm) struct task_struct *tsk = current; task_lock(tsk); - tsk->flags &= ~PF_BORROWED_MM; tsk->mm = NULL; /* active_mm is still 'mm' */ enter_lazy_tlb(mm, tsk); diff --git a/include/linux/sched.h b/include/linux/sched.h index eec64a4adb9d..0560999eb1db 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1483,7 +1483,6 @@ static inline void put_task_struct(struct task_struct *t) #define PF_EXITING 0x00000004 /* getting shut down */ #define PF_EXITPIDONE 0x00000008 /* pi exit done on shut down */ #define PF_VCPU 0x00000010 /* I'm a virtual CPU */ -#define PF_KTHREAD 0x00000020 /* I am a kernel thread */ #define PF_FORKNOEXEC 0x00000040 /* forked but didn't exec */ #define PF_SUPERPRIV 0x00000100 /* used super-user privileges */ #define PF_DUMPCORE 0x00000200 /* dumped core */ @@ -1497,7 +1496,7 @@ static inline void put_task_struct(struct task_struct *t) #define PF_KSWAPD 0x00040000 /* I am kswapd */ #define PF_SWAPOFF 0x00080000 /* I am in swapoff */ #define PF_LESS_THROTTLE 0x00100000 /* Throttle me less: I clean memory */ -#define PF_BORROWED_MM 0x00200000 /* I am a kthread doing use_mm */ +#define PF_KTHREAD 0x00200000 /* I am a kernel thread */ #define PF_RANDOMIZE 0x00400000 /* randomize virtual address space */ #define PF_SWAPWRITE 0x00800000 /* Allowed to write to swap */ #define PF_SPREAD_PAGE 0x01000000 /* Spread page cache over cpuset */ diff --git a/kernel/fork.c b/kernel/fork.c index 228f80c9155a..eeaec6893b0d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -474,7 +474,7 @@ EXPORT_SYMBOL_GPL(mmput); /** * get_task_mm - acquire a reference to the task's mm * - * Returns %NULL if the task has no mm. Checks PF_BORROWED_MM (meaning + * Returns %NULL if the task has no mm. Checks PF_KTHREAD (meaning * this kernel workthread has transiently adopted a user mm with use_mm, * to do its AIO) is not set and if so returns a reference to it, after * bumping up the use count. User must release the mm via mmput() @@ -487,7 +487,7 @@ struct mm_struct *get_task_mm(struct task_struct *task) task_lock(task); mm = task->mm; if (mm) { - if (task->flags & PF_BORROWED_MM) + if (task->flags & PF_KTHREAD) mm = NULL; else atomic_inc(&mm->mm_users); -- cgit From 32ecb1f26dd50eeaac4e3f4dea4541c97848e459 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:41 -0700 Subject: coredump: turn mm->core_startup_done into the pointer to struct core_state mm->core_startup_done points to "struct completion startup_done" allocated on the coredump_wait()'s stack. Introduce the new structure, core_state, which holds this "struct completion". This way we can add more info visible to the threads participating in coredump without enlarging mm_struct. No changes in affected .o files. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 8 ++++---- include/linux/mm_types.h | 7 ++++++- kernel/exit.c | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index e347e6ed1617..71734568f018 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1597,13 +1597,13 @@ static int coredump_wait(int exit_code) { struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; - struct completion startup_done; + struct core_state core_state; struct completion *vfork_done; int core_waiters; init_completion(&mm->core_done); - init_completion(&startup_done); - mm->core_startup_done = &startup_done; + init_completion(&core_state.startup); + mm->core_state = &core_state; core_waiters = zap_threads(tsk, mm, exit_code); up_write(&mm->mmap_sem); @@ -1622,7 +1622,7 @@ static int coredump_wait(int exit_code) } if (core_waiters) - wait_for_completion(&startup_done); + wait_for_completion(&core_state.startup); fail: BUG_ON(mm->core_waiters); return core_waiters; diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 02a27ae78539..97819efd2333 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -159,6 +159,10 @@ struct vm_area_struct { #endif }; +struct core_state { + struct completion startup; +}; + struct mm_struct { struct vm_area_struct * mmap; /* list of VMAs */ struct rb_root mm_rb; @@ -220,7 +224,8 @@ struct mm_struct { unsigned long flags; /* Must use atomic bitops to access the bits */ /* coredumping support */ - struct completion *core_startup_done, core_done; + struct core_state *core_state; + struct completion core_done; /* aio bits */ rwlock_t ioctx_list_lock; /* aio lock */ diff --git a/kernel/exit.c b/kernel/exit.c index 28a44a2612dc..f7fa21dbced4 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -680,7 +680,7 @@ static void exit_mm(struct task_struct * tsk) up_read(&mm->mmap_sem); down_write(&mm->mmap_sem); if (!--mm->core_waiters) - complete(mm->core_startup_done); + complete(&mm->core_state->startup); up_write(&mm->mmap_sem); wait_for_completion(&mm->core_done); -- cgit From 999d9fc1670bc082928b93b11d1f2e0e417d973c Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:41 -0700 Subject: coredump: move mm->core_waiters into struct core_state Move mm->core_waiters into "struct core_state" allocated on stack. This shrinks mm_struct a little bit and allows further changes. This patch mostly does s/core_waiters/core_state. The only essential change is that coredump_wait() must clear mm->core_state before return. The coredump_wait()'s path is uglified and .text grows by 30 bytes, this is fixed by the next patch. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 21 +++++++++++---------- include/linux/mm_types.h | 2 +- kernel/exit.c | 8 ++++---- kernel/fork.c | 2 +- kernel/signal.c | 4 ++-- 5 files changed, 19 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index 71734568f018..50de3aaff4d0 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -722,12 +722,10 @@ static int exec_mmap(struct mm_struct *mm) * Make sure that if there is a core dump in progress * for the old mm, we get out and die instead of going * through with the exec. We must hold mmap_sem around - * checking core_waiters and changing tsk->mm. The - * core-inducing thread will increment core_waiters for - * each thread whose ->mm == old_mm. + * checking core_state and changing tsk->mm. */ down_read(&old_mm->mmap_sem); - if (unlikely(old_mm->core_waiters)) { + if (unlikely(old_mm->core_state)) { up_read(&old_mm->mmap_sem); return -EINTR; } @@ -1514,7 +1512,7 @@ static void zap_process(struct task_struct *start) t = start; do { if (t != current && t->mm) { - t->mm->core_waiters++; + t->mm->core_state->nr_threads++; sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); } @@ -1538,11 +1536,11 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm, if (err) return err; - if (atomic_read(&mm->mm_users) == mm->core_waiters + 1) + if (atomic_read(&mm->mm_users) == mm->core_state->nr_threads + 1) goto done; /* * We should find and kill all tasks which use this mm, and we should - * count them correctly into mm->core_waiters. We don't take tasklist + * count them correctly into ->nr_threads. We don't take tasklist * lock, but this is safe wrt: * * fork: @@ -1590,7 +1588,7 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm, } rcu_read_unlock(); done: - return mm->core_waiters; + return mm->core_state->nr_threads; } static int coredump_wait(int exit_code) @@ -1603,9 +1601,12 @@ static int coredump_wait(int exit_code) init_completion(&mm->core_done); init_completion(&core_state.startup); + core_state.nr_threads = 0; mm->core_state = &core_state; core_waiters = zap_threads(tsk, mm, exit_code); + if (core_waiters < 0) + mm->core_state = NULL; up_write(&mm->mmap_sem); if (unlikely(core_waiters < 0)) @@ -1623,8 +1624,8 @@ static int coredump_wait(int exit_code) if (core_waiters) wait_for_completion(&core_state.startup); + mm->core_state = NULL; fail: - BUG_ON(mm->core_waiters); return core_waiters; } @@ -1702,7 +1703,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) /* * If another thread got here first, or we are not dumpable, bail out. */ - if (mm->core_waiters || !get_dumpable(mm)) { + if (mm->core_state || !get_dumpable(mm)) { up_write(&mm->mmap_sem); goto fail; } diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 97819efd2333..c0b1747b61a5 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -160,6 +160,7 @@ struct vm_area_struct { }; struct core_state { + int nr_threads; struct completion startup; }; @@ -179,7 +180,6 @@ struct mm_struct { atomic_t mm_users; /* How many users with user space? */ atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */ int map_count; /* number of VMAs */ - int core_waiters; struct rw_semaphore mmap_sem; spinlock_t page_table_lock; /* Protects page tables and some counters */ diff --git a/kernel/exit.c b/kernel/exit.c index f7fa21dbced4..988e232254e9 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -670,16 +670,16 @@ static void exit_mm(struct task_struct * tsk) return; /* * Serialize with any possible pending coredump. - * We must hold mmap_sem around checking core_waiters + * We must hold mmap_sem around checking core_state * and clearing tsk->mm. The core-inducing thread - * will increment core_waiters for each thread in the + * will increment ->nr_threads for each thread in the * group with ->mm != NULL. */ down_read(&mm->mmap_sem); - if (mm->core_waiters) { + if (mm->core_state) { up_read(&mm->mmap_sem); down_write(&mm->mmap_sem); - if (!--mm->core_waiters) + if (!--mm->core_state->nr_threads) complete(&mm->core_state->startup); up_write(&mm->mmap_sem); diff --git a/kernel/fork.c b/kernel/fork.c index eeaec6893b0d..813d5c89b9d5 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -400,7 +400,7 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p) INIT_LIST_HEAD(&mm->mmlist); mm->flags = (current->mm) ? current->mm->flags : MMF_DUMP_FILTER_DEFAULT; - mm->core_waiters = 0; + mm->core_state = NULL; mm->nr_ptes = 0; set_mm_counter(mm, file_rss, 0); set_mm_counter(mm, anon_rss, 0); diff --git a/kernel/signal.c b/kernel/signal.c index 39c1706edf03..5c7b7eaa0dc6 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1480,10 +1480,10 @@ static inline int may_ptrace_stop(void) * is a deadlock situation, and pointless because our tracer * is dead so don't allow us to stop. * If SIGKILL was already sent before the caller unlocked - * ->siglock we must see ->core_waiters != 0. Otherwise it + * ->siglock we must see ->core_state != NULL. Otherwise it * is safe to enter schedule(). */ - if (unlikely(current->mm->core_waiters) && + if (unlikely(current->mm->core_state) && unlikely(current->mm == current->parent->mm)) return 0; -- cgit From c5f1cc8c1828486a61ab3e575da6e2c62b34d399 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:42 -0700 Subject: coredump: turn core_state->nr_threads into atomic_t Turn core_state->nr_threads into atomic_t and kill now unneeded down_write(&mm->mmap_sem) in exit_mm(). Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 2 +- include/linux/mm_types.h | 2 +- kernel/exit.c | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index c74bb34eeeff..15d493fe8aa3 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1591,7 +1591,7 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm, } rcu_read_unlock(); done: - core_state->nr_threads = nr; + atomic_set(&core_state->nr_threads, nr); return nr; } diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index c0b1747b61a5..ae99a28ba6ae 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -160,7 +160,7 @@ struct vm_area_struct { }; struct core_state { - int nr_threads; + atomic_t nr_threads; struct completion startup; }; diff --git a/kernel/exit.c b/kernel/exit.c index 988e232254e9..63d82957baae 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -678,10 +678,9 @@ static void exit_mm(struct task_struct * tsk) down_read(&mm->mmap_sem); if (mm->core_state) { up_read(&mm->mmap_sem); - down_write(&mm->mmap_sem); - if (!--mm->core_state->nr_threads) + + if (atomic_dec_and_test(&mm->core_state->nr_threads)) complete(&mm->core_state->startup); - up_write(&mm->mmap_sem); wait_for_completion(&mm->core_done); down_read(&mm->mmap_sem); -- cgit From b564daf806d492dd4f7afe9b6c83b8d35d137669 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:44 -0700 Subject: coredump: construct the list of coredumping threads at startup time binfmt->core_dump() has to iterate over the all threads in system in order to find the coredumping threads and construct the list using the GFP_ATOMIC allocations. With this patch each thread allocates the list node on exit_mm()'s stack and adds itself to the list. This allows us to do further changes: - simplify ->core_dump() - change exit_mm() to clear ->mm first, then wait for ->core_done. this makes the coredumping process visible to oom_kill - kill mm->core_done Signed-off-by: Oleg Nesterov Acked-by: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 2 ++ include/linux/mm_types.h | 6 ++++++ kernel/exit.c | 15 ++++++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index b8ee842d93cd..fe2873b8037f 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1604,6 +1604,8 @@ static int coredump_wait(int exit_code, struct core_state *core_state) init_completion(&mm->core_done); init_completion(&core_state->startup); + core_state->dumper.task = tsk; + core_state->dumper.next = NULL; core_waiters = zap_threads(tsk, mm, core_state, exit_code); up_write(&mm->mmap_sem); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index ae99a28ba6ae..4d0d0abc79fe 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -159,8 +159,14 @@ struct vm_area_struct { #endif }; +struct core_thread { + struct task_struct *task; + struct core_thread *next; +}; + struct core_state { atomic_t nr_threads; + struct core_thread dumper; struct completion startup; }; diff --git a/kernel/exit.c b/kernel/exit.c index 63d82957baae..b66f0d55c791 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -664,6 +664,7 @@ assign_new_owner: static void exit_mm(struct task_struct * tsk) { struct mm_struct *mm = tsk->mm; + struct core_state *core_state; mm_release(tsk, mm); if (!mm) @@ -676,11 +677,19 @@ static void exit_mm(struct task_struct * tsk) * group with ->mm != NULL. */ down_read(&mm->mmap_sem); - if (mm->core_state) { + core_state = mm->core_state; + if (core_state) { + struct core_thread self; up_read(&mm->mmap_sem); - if (atomic_dec_and_test(&mm->core_state->nr_threads)) - complete(&mm->core_state->startup); + self.task = tsk; + self.next = xchg(&core_state->dumper.next, &self); + /* + * Implies mb(), the result of xchg() must be visible + * to core_state->dumper. + */ + if (atomic_dec_and_test(&core_state->nr_threads)) + complete(&core_state->startup); wait_for_completion(&mm->core_done); down_read(&mm->mmap_sem); -- cgit From a94e2d408eaedbd85aae259621d46fafc10479a2 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:46 -0700 Subject: coredump: kill mm->core_done Now that we have core_state->dumper list we can use it to wake up the sub-threads waiting for the coredump completion. This uglifies the code and .text grows by 47 bytes, but otoh mm_struct lessens by sizeof(struct completion). Also, with this change we can decouple exit_mm() from the coredumping code. Signed-off-by: Oleg Nesterov Cc: Roland McGrath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 25 ++++++++++++++++++++++--- include/linux/mm_types.h | 4 +--- kernel/exit.c | 8 +++++++- 3 files changed, 30 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index fe2873b8037f..bff43aeb235e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1602,7 +1602,6 @@ static int coredump_wait(int exit_code, struct core_state *core_state) struct completion *vfork_done; int core_waiters; - init_completion(&mm->core_done); init_completion(&core_state->startup); core_state->dumper.task = tsk; core_state->dumper.next = NULL; @@ -1628,6 +1627,27 @@ fail: return core_waiters; } +static void coredump_finish(struct mm_struct *mm) +{ + struct core_thread *curr, *next; + struct task_struct *task; + + next = mm->core_state->dumper.next; + while ((curr = next) != NULL) { + next = curr->next; + task = curr->task; + /* + * see exit_mm(), curr->task must not see + * ->task == NULL before we read ->next. + */ + smp_mb(); + curr->task = NULL; + wake_up_process(task); + } + + mm->core_state = NULL; +} + /* * set_dumpable converts traditional three-value dumpable to two flags and * stores them into mm->flags. It modifies lower two bits of mm->flags, but @@ -1812,8 +1832,7 @@ fail_unlock: argv_free(helper_argv); current->fsuid = fsuid; - complete_all(&mm->core_done); - mm->core_state = NULL; + coredump_finish(mm); fail: return retval; } diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 4d0d0abc79fe..746f975b58ef 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -229,9 +229,7 @@ struct mm_struct { unsigned long flags; /* Must use atomic bitops to access the bits */ - /* coredumping support */ - struct core_state *core_state; - struct completion core_done; + struct core_state *core_state; /* coredumping support */ /* aio bits */ rwlock_t ioctx_list_lock; /* aio lock */ diff --git a/kernel/exit.c b/kernel/exit.c index b66f0d55c791..8a4d4d12e294 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -691,7 +691,13 @@ static void exit_mm(struct task_struct * tsk) if (atomic_dec_and_test(&core_state->nr_threads)) complete(&core_state->startup); - wait_for_completion(&mm->core_done); + for (;;) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (!self.task) /* see coredump_finish() */ + break; + schedule(); + } + __set_task_state(tsk, TASK_RUNNING); down_read(&mm->mmap_sem); } atomic_inc(&mm->mm_count); -- cgit From db700897224b5ebdf852f2d38920ce428940d059 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:49 -0700 Subject: workqueues: implement flush_work() Most of users of flush_workqueue() can be changed to use cancel_work_sync(), but sometimes we really need to wait for the completion and cancelling is not an option. schedule_on_each_cpu() is good example. Add the new helper, flush_work(work), which waits for the completion of the specific work_struct. More precisely, it "flushes" the result of of the last queue_work() which is visible to the caller. For example, this code queue_work(wq, work); /* WINDOW */ queue_work(wq, work); flush_work(work); doesn't necessary work "as expected". What can happen in the WINDOW above is - wq starts the execution of work->func() - the caller migrates to another CPU now, after the 2nd queue_work() this work is active on the previous CPU, and at the same time it is queued on another. In this case flush_work(work) may return before the first work->func() completes. It is trivial to add another helper int flush_work_sync(struct work_struct *work) { return flush_work(work) || wait_on_work(work); } which works "more correctly", but it has to iterate over all CPUs and thus it much slower than flush_work(). Signed-off-by: Oleg Nesterov Acked-by: Max Krasnyansky Acked-by: Jarek Poplawski Cc: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/workqueue.h | 2 ++ kernel/workqueue.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'include/linux') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 14d47120682b..5c158c477ac7 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -201,6 +201,8 @@ extern int keventd_up(void); extern void init_workqueues(void); int execute_in_process_context(work_func_t fn, struct execute_work *); +extern int flush_work(struct work_struct *work); + extern int cancel_work_sync(struct work_struct *work); /* diff --git a/kernel/workqueue.c b/kernel/workqueue.c index d9a2d65cc63e..ee41cf857d55 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -423,6 +423,52 @@ void flush_workqueue(struct workqueue_struct *wq) } EXPORT_SYMBOL_GPL(flush_workqueue); +/** + * flush_work - block until a work_struct's callback has terminated + * @work: the work which is to be flushed + * + * It is expected that, prior to calling flush_work(), the caller has + * arranged for the work to not be requeued, otherwise it doesn't make + * sense to use this function. + */ +int flush_work(struct work_struct *work) +{ + struct cpu_workqueue_struct *cwq; + struct list_head *prev; + struct wq_barrier barr; + + might_sleep(); + cwq = get_wq_data(work); + if (!cwq) + return 0; + + prev = NULL; + spin_lock_irq(&cwq->lock); + if (!list_empty(&work->entry)) { + /* + * See the comment near try_to_grab_pending()->smp_rmb(). + * If it was re-queued under us we are not going to wait. + */ + smp_rmb(); + if (unlikely(cwq != get_wq_data(work))) + goto out; + prev = &work->entry; + } else { + if (cwq->current_work != work) + goto out; + prev = &cwq->worklist; + } + insert_wq_barrier(cwq, &barr, prev->next); +out: + spin_unlock_irq(&cwq->lock); + if (!prev) + return 0; + + wait_for_completion(&barr.done); + return 1; +} +EXPORT_SYMBOL_GPL(flush_work); + /* * Upon a successful return (>= 0), the caller "owns" WORK_STRUCT_PENDING bit, * so this work can't be re-armed in any way. -- cgit From 3da1c84c00c7e5fa8348336bd8c342f9128b0f14 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 25 Jul 2008 01:47:50 -0700 Subject: workqueues: make get_online_cpus() useable for work->func() workqueue_cpu_callback(CPU_DEAD) flushes cwq->thread under cpu_maps_update_begin(). This means that the multithreaded workqueues can't use get_online_cpus() due to the possible deadlock, very bad and very old problem. Introduce the new state, CPU_POST_DEAD, which is called after cpu_hotplug_done() but before cpu_maps_update_done(). Change workqueue_cpu_callback() to use CPU_POST_DEAD instead of CPU_DEAD. This means that create/destroy functions can't rely on get_online_cpus() any longer and should take cpu_add_remove_lock instead. [akpm@linux-foundation.org: fix CONFIG_SMP=n] Signed-off-by: Oleg Nesterov Acked-by: Gautham R Shenoy Cc: Heiko Carstens Cc: Max Krasnyansky Cc: Paul Jackson Cc: Paul Menage Cc: Peter Zijlstra Cc: Vegard Nossum Cc: Martin Schwidefsky Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cpu.h | 15 +++++++++++---- include/linux/notifier.h | 2 ++ kernel/cpu.c | 5 +++++ kernel/workqueue.c | 18 +++++++++--------- 4 files changed, 27 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 7464ba3b4333..d7faf8808497 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -69,10 +69,11 @@ static inline void unregister_cpu_notifier(struct notifier_block *nb) #endif int cpu_up(unsigned int cpu); - extern void cpu_hotplug_init(void); +extern void cpu_maps_update_begin(void); +extern void cpu_maps_update_done(void); -#else +#else /* CONFIG_SMP */ static inline int register_cpu_notifier(struct notifier_block *nb) { @@ -87,10 +88,16 @@ static inline void cpu_hotplug_init(void) { } +static inline void cpu_maps_update_begin(void) +{ +} + +static inline void cpu_maps_update_done(void) +{ +} + #endif /* CONFIG_SMP */ extern struct sysdev_class cpu_sysdev_class; -extern void cpu_maps_update_begin(void); -extern void cpu_maps_update_done(void); #ifdef CONFIG_HOTPLUG_CPU /* Stop CPUs going up and down. */ diff --git a/include/linux/notifier.h b/include/linux/notifier.h index bd3d72ddf333..da2698b0fdd1 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -214,6 +214,8 @@ static inline int notifier_to_errno(int ret) #define CPU_DEAD 0x0007 /* CPU (unsigned)v dead */ #define CPU_DYING 0x0008 /* CPU (unsigned)v not running any task, * not handling interrupts, soon dead */ +#define CPU_POST_DEAD 0x0009 /* CPU (unsigned)v dead, cpu_hotplug + * lock is dropped */ /* Used for CPU hotplug events occuring while tasks are frozen due to a suspend * operation in progress diff --git a/kernel/cpu.c b/kernel/cpu.c index 2cc409ce0a8f..10ba5f1004a5 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -285,6 +285,11 @@ out_allowed: set_cpus_allowed_ptr(current, &old_allowed); out_release: cpu_hotplug_done(); + if (!err) { + if (raw_notifier_call_chain(&cpu_chain, CPU_POST_DEAD | mod, + hcpu) == NOTIFY_BAD) + BUG(); + } return err; } diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 5fbffd302eb5..828e58230cbc 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -828,7 +828,7 @@ struct workqueue_struct *__create_workqueue_key(const char *name, err = create_workqueue_thread(cwq, singlethread_cpu); start_workqueue_thread(cwq, -1); } else { - get_online_cpus(); + cpu_maps_update_begin(); spin_lock(&workqueue_lock); list_add(&wq->list, &workqueues); spin_unlock(&workqueue_lock); @@ -840,7 +840,7 @@ struct workqueue_struct *__create_workqueue_key(const char *name, err = create_workqueue_thread(cwq, cpu); start_workqueue_thread(cwq, cpu); } - put_online_cpus(); + cpu_maps_update_done(); } if (err) { @@ -854,8 +854,8 @@ EXPORT_SYMBOL_GPL(__create_workqueue_key); static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq) { /* - * Our caller is either destroy_workqueue() or CPU_DEAD, - * get_online_cpus() protects cwq->thread. + * Our caller is either destroy_workqueue() or CPU_POST_DEAD, + * cpu_add_remove_lock protects cwq->thread. */ if (cwq->thread == NULL) return; @@ -865,7 +865,7 @@ static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq) flush_cpu_workqueue(cwq); /* - * If the caller is CPU_DEAD and cwq->worklist was not empty, + * If the caller is CPU_POST_DEAD and cwq->worklist was not empty, * a concurrent flush_workqueue() can insert a barrier after us. * However, in that case run_workqueue() won't return and check * kthread_should_stop() until it flushes all work_struct's. @@ -889,14 +889,14 @@ void destroy_workqueue(struct workqueue_struct *wq) const cpumask_t *cpu_map = wq_cpu_map(wq); int cpu; - get_online_cpus(); + cpu_maps_update_begin(); spin_lock(&workqueue_lock); list_del(&wq->list); spin_unlock(&workqueue_lock); for_each_cpu_mask_nr(cpu, *cpu_map) cleanup_workqueue_thread(per_cpu_ptr(wq->cpu_wq, cpu)); - put_online_cpus(); + cpu_maps_update_done(); free_percpu(wq->cpu_wq); kfree(wq); @@ -935,7 +935,7 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, case CPU_UP_CANCELED: start_workqueue_thread(cwq, -1); - case CPU_DEAD: + case CPU_POST_DEAD: cleanup_workqueue_thread(cwq); break; } @@ -943,7 +943,7 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_UP_CANCELED: - case CPU_DEAD: + case CPU_POST_DEAD: cpu_clear(cpu, cpu_populated_map); } -- cgit From 95b68dec0d52c7b8fea3698b3938cf3ab936436b Mon Sep 17 00:00:00 2001 From: Chandru Date: Fri, 25 Jul 2008 01:47:55 -0700 Subject: calgary iommu: use the first kernels TCE tables in kdump kdump kernel fails to boot with calgary iommu and aacraid driver on a x366 box. The ongoing dma's of aacraid from the first kernel continue to exist until the driver is loaded in the kdump kernel. Calgary is initialized prior to aacraid and creation of new tce tables causes wrong dma's to occur. Here we try to get the tce tables of the first kernel in kdump kernel and use them. While in the kdump kernel we do not allocate new tce tables but instead read the base address register contents of calgary iommu and use the tables that the registers point to. With these changes the kdump kernel and hence aacraid now boots normally. Signed-off-by: Chandru Siddalingappa Acked-by: Muli Ben-Yehuda Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/kernel/pci-calgary_64.c | 85 +++++++++++++++++++++++++++++++++++++--- include/linux/crash_dump.h | 8 ++++ 2 files changed, 87 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c index 151f2d171f7c..19e7fc7c2c4f 100644 --- a/arch/x86/kernel/pci-calgary_64.c +++ b/arch/x86/kernel/pci-calgary_64.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -167,6 +168,8 @@ static void calgary_dump_error_regs(struct iommu_table *tbl); static void calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev); static void calioc2_tce_cache_blast(struct iommu_table *tbl); static void calioc2_dump_error_regs(struct iommu_table *tbl); +static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl); +static void get_tce_space_from_tar(void); static struct cal_chipset_ops calgary_chip_ops = { .handle_quirks = calgary_handle_quirks, @@ -830,7 +833,11 @@ static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar) tbl = pci_iommu(dev->bus); tbl->it_base = (unsigned long)bus_info[dev->bus->number].tce_space; - tce_free(tbl, 0, tbl->it_size); + + if (is_kdump_kernel()) + calgary_init_bitmap_from_tce_table(tbl); + else + tce_free(tbl, 0, tbl->it_size); if (is_calgary(dev->device)) tbl->chip_ops = &calgary_chip_ops; @@ -1209,6 +1216,10 @@ static int __init calgary_init(void) if (ret) return ret; + /* Purely for kdump kernel case */ + if (is_kdump_kernel()) + get_tce_space_from_tar(); + do { dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev); if (!dev) @@ -1339,6 +1350,61 @@ static int __init calgary_bus_has_devices(int bus, unsigned short pci_dev) return (val != 0xffffffff); } +/* + * calgary_init_bitmap_from_tce_table(): + * Funtion for kdump case. In the second/kdump kernel initialize + * the bitmap based on the tce table entries obtained from first kernel + */ +static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl) +{ + u64 *tp; + unsigned int index; + tp = ((u64 *)tbl->it_base); + for (index = 0 ; index < tbl->it_size; index++) { + if (*tp != 0x0) + set_bit(index, tbl->it_map); + tp++; + } +} + +/* + * get_tce_space_from_tar(): + * Function for kdump case. Get the tce tables from first kernel + * by reading the contents of the base adress register of calgary iommu + */ +static void get_tce_space_from_tar() +{ + int bus; + void __iomem *target; + unsigned long tce_space; + + for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { + struct calgary_bus_info *info = &bus_info[bus]; + unsigned short pci_device; + u32 val; + + val = read_pci_config(bus, 0, 0, 0); + pci_device = (val & 0xFFFF0000) >> 16; + + if (!is_cal_pci_dev(pci_device)) + continue; + if (info->translation_disabled) + continue; + + if (calgary_bus_has_devices(bus, pci_device) || + translate_empty_slots) { + target = calgary_reg(bus_info[bus].bbar, + tar_offset(bus)); + tce_space = be64_to_cpu(readq(target)); + tce_space = tce_space & TAR_SW_BITS; + + tce_space = tce_space & (~specified_table_size); + info->tce_space = (u64 *)__va(tce_space); + } + } + return; +} + void __init detect_calgary(void) { int bus; @@ -1394,7 +1460,8 @@ void __init detect_calgary(void) return; } - specified_table_size = determine_tce_table_size(max_pfn * PAGE_SIZE); + specified_table_size = determine_tce_table_size((is_kdump_kernel() ? + saved_max_pfn : max_pfn) * PAGE_SIZE); for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { struct calgary_bus_info *info = &bus_info[bus]; @@ -1412,10 +1479,16 @@ void __init detect_calgary(void) if (calgary_bus_has_devices(bus, pci_device) || translate_empty_slots) { - tbl = alloc_tce_table(); - if (!tbl) - goto cleanup; - info->tce_space = tbl; + /* + * If it is kdump kernel, find and use tce tables + * from first kernel, else allocate tce tables here + */ + if (!is_kdump_kernel()) { + tbl = alloc_tce_table(); + if (!tbl) + goto cleanup; + info->tce_space = tbl; + } calgary_found = 1; } } diff --git a/include/linux/crash_dump.h b/include/linux/crash_dump.h index 22c7ac5cd80c..6cd39a927e1f 100644 --- a/include/linux/crash_dump.h +++ b/include/linux/crash_dump.h @@ -22,5 +22,13 @@ extern struct proc_dir_entry *proc_vmcore; #define vmcore_elf_check_arch(x) (elf_check_arch(x) || vmcore_elf_check_arch_cross(x)) +static inline int is_kdump_kernel(void) +{ + return (elfcorehdr_addr != ELFCORE_ADDR_MAX) ? 1 : 0; +} +#else /* !CONFIG_CRASH_DUMP */ +static inline int is_kdump_kernel(void) { return 0; } #endif /* CONFIG_CRASH_DUMP */ + +extern unsigned long saved_max_pfn; #endif /* LINUX_CRASHDUMP_H */ -- cgit From 2027d1abc25ff770cc3bc936abd33570ce85d85a Mon Sep 17 00:00:00 2001 From: Nadia Derbey Date: Fri, 25 Jul 2008 01:47:57 -0700 Subject: idr: change the idr structure After scalability problems have been detected when using the sysV ipcs, I have proposed to use an RCU based implementation of the IDR api instead (see threads http://lkml.org/lkml/2008/4/11/212 and http://lkml.org/lkml/2008/4/29/295). This resulted in many people asking to convert the idr API and make it rcu safe (because most of the code was duplicated and thus unmaintanable and unreviewable). So here is a first attempt. The important change wrt to the idr API itself is during idr removes: idr layers are freed after a grace period, instead of being moved to the free list. The important change wrt to ipcs, is that idr_find() can now be called locklessly inside a rcu read critical section. Here are the results I've got for the pmsg test sent by Manfred: 2.6.25-rc3-mm1 2.6.25-rc3-mm1+ 2.6.25-mm1 Patched 2.6.25-mm1 1 1168441 1064021 876000 947488 2 1094264 921059 1549592 1730685 3 2082520 1738165 1694370 2324880 4 2079929 1695521 404553 2400408 5 2898758 406566 391283 3246580 6 2921417 261275 263249 3752148 7 3308761 126056 191742 4243142 8 3329456 100129 141722 4275780 1st column: stock 2.6.25-rc3-mm1 2nd column: 2.6.25-rc3-mm1 + ipc patches (store ipcs into idrs) 3nd column: stock 2.6.25-mm1 4th column: 2.6.25-mm1 + this pacth series. This patch: Add an rcu_head to the idr_layer structure in order to free it after a grace period. Signed-off-by: Nadia Derbey Reviewed-by: "Paul E. McKenney" Cc: Manfred Spraul Cc: Jim Houston Cc: Pierre Peiffer Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/idr.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/idr.h b/include/linux/idr.h index 9a2d762124de..1af61d23be36 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -15,6 +15,7 @@ #include #include #include +#include #if BITS_PER_LONG == 32 # define IDR_BITS 5 @@ -51,6 +52,7 @@ struct idr_layer { unsigned long bitmap; /* A zero bit means "space here" */ struct idr_layer *ary[1< Date: Fri, 25 Jul 2008 01:47:59 -0700 Subject: idr: error checking factorization Do some code factorization in the return code analysis. Signed-off-by: Nadia Derbey Cc: "Paul E. McKenney" Cc: Manfred Spraul Cc: Jim Houston Cc: Pierre Peiffer Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/idr.h | 6 ++++++ lib/idr.c | 30 +++++++++--------------------- 2 files changed, 15 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/idr.h b/include/linux/idr.h index 1af61d23be36..762c3f2c631d 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -73,6 +73,12 @@ struct idr { } #define DEFINE_IDR(name) struct idr name = IDR_INIT(name) +/* Actions to be taken after a call to _idr_sub_alloc */ +#define IDR_NEED_TO_GROW -2 +#define IDR_NOMORE_SPACE -3 + +#define _idr_rc_to_errno(rc) ((rc) == -1 ? -EAGAIN : -ENOSPC) + /* * This is what we export. */ diff --git a/lib/idr.c b/lib/idr.c index 9d905b131ecb..80ba06f29d36 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -143,7 +143,7 @@ static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa) /* if already at the top layer, we need to grow */ if (!(p = pa[l])) { *starting_id = id; - return -2; + return IDR_NEED_TO_GROW; } /* If we need to go up one layer, continue the @@ -160,7 +160,7 @@ static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa) id = ((id >> sh) ^ n ^ m) << sh; } if ((id >= MAX_ID_BIT) || (id < 0)) - return -3; + return IDR_NOMORE_SPACE; if (l == 0) break; /* @@ -229,7 +229,7 @@ build_up: idp->top = p; idp->layers = layers; v = sub_alloc(idp, &id, pa); - if (v == -2) + if (v == IDR_NEED_TO_GROW) goto build_up; return(v); } @@ -278,12 +278,8 @@ int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id) * This is a cheap hack until the IDR code can be fixed to * return proper error values. */ - if (rv < 0) { - if (rv == -1) - return -EAGAIN; - else /* Will be -3 */ - return -ENOSPC; - } + if (rv < 0) + return _idr_rc_to_errno(rv); *id = rv; return 0; } @@ -313,12 +309,8 @@ int idr_get_new(struct idr *idp, void *ptr, int *id) * This is a cheap hack until the IDR code can be fixed to * return proper error values. */ - if (rv < 0) { - if (rv == -1) - return -EAGAIN; - else /* Will be -3 */ - return -ENOSPC; - } + if (rv < 0) + return _idr_rc_to_errno(rv); *id = rv; return 0; } @@ -696,12 +688,8 @@ int ida_get_new_above(struct ida *ida, int starting_id, int *p_id) restart: /* get vacant slot */ t = idr_get_empty_slot(&ida->idr, idr_id, pa); - if (t < 0) { - if (t == -1) - return -EAGAIN; - else /* will be -3 */ - return -ENOSPC; - } + if (t < 0) + return _idr_rc_to_errno(t); if (t * IDA_BITMAP_BITS >= MAX_ID_BIT) return -ENOSPC; -- cgit From f9c46d6ea5ce138a886c3a0f10a46130afab75f5 Mon Sep 17 00:00:00 2001 From: Nadia Derbey Date: Fri, 25 Jul 2008 01:48:01 -0700 Subject: idr: make idr_find rcu-safe Make idr_find rcu-safe: it can now be called inside an rcu_read critical section. Signed-off-by: Nadia Derbey Reviewed-by: "Paul E. McKenney" Cc: Manfred Spraul Cc: Jim Houston Cc: Pierre Peiffer Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/idr.h | 16 ++++++++++++++++ lib/idr.c | 11 ++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/idr.h b/include/linux/idr.h index 762c3f2c631d..fa035f96f2a3 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -79,6 +79,22 @@ struct idr { #define _idr_rc_to_errno(rc) ((rc) == -1 ? -EAGAIN : -ENOSPC) +/** + * idr synchronization (stolen from radix-tree.h) + * + * idr_find() is able to be called locklessly, using RCU. The caller must + * ensure calls to this function are made within rcu_read_lock() regions. + * Other readers (lock-free or otherwise) and modifications may be running + * concurrently. + * + * It is still required that the caller manage the synchronization and + * lifetimes of the items. So if RCU lock-free lookups are used, typically + * this would mean that the items have their own locks, or are amenable to + * lock-free access; and that the items are freed by RCU (or only freed after + * having been deleted from the idr tree *and* a synchronize_rcu() grace + * period). + */ + /* * This is what we export. */ diff --git a/lib/idr.c b/lib/idr.c index 44ab3b2a4eba..21e12af1f231 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -456,7 +456,8 @@ EXPORT_SYMBOL(idr_destroy); * return indicates that @id is not valid or you passed %NULL in * idr_get_new(). * - * The caller must serialize idr_find() vs idr_get_new() and idr_remove(). + * This function can be called under rcu_read_lock(), given that the leaf + * pointers lifetimes are correctly managed. */ void *idr_find(struct idr *idp, int id) { @@ -464,7 +465,7 @@ void *idr_find(struct idr *idp, int id) struct idr_layer *p; n = idp->layers * IDR_BITS; - p = idp->top; + p = rcu_dereference(idp->top); /* Mask off upper bits we don't use for the search. */ id &= MAX_ID_MASK; @@ -474,7 +475,7 @@ void *idr_find(struct idr *idp, int id) while (n > 0 && p) { n -= IDR_BITS; - p = p->ary[(id >> n) & IDR_MASK]; + p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]); } return((void *)p); } @@ -507,7 +508,7 @@ int idr_for_each(struct idr *idp, struct idr_layer **paa = &pa[0]; n = idp->layers * IDR_BITS; - p = idp->top; + p = rcu_dereference(idp->top); max = 1 << n; id = 0; @@ -515,7 +516,7 @@ int idr_for_each(struct idr *idp, while (n > 0 && p) { n -= IDR_BITS; *paa++ = p; - p = p->ary[(id >> n) & IDR_MASK]; + p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]); } if (p) { -- cgit From 4daa28f6d8f5cda8ea0f55048e3c8811c384cbdd Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Fri, 25 Jul 2008 01:48:04 -0700 Subject: ipc/sem.c: convert undo structures to struct list_head The undo structures contain two linked lists, the attached patch replaces them with generic struct list_head lists. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Manfred Spraul Cc: Nadia Derbey Cc: Pierre Peiffer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sem.h | 12 ++-- ipc/sem.c | 163 ++++++++++++++++++++++++++++------------------------ 2 files changed, 95 insertions(+), 80 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sem.h b/include/linux/sem.h index c8eaad9e4b72..6a1af1b49a13 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -95,7 +95,7 @@ struct sem_array { struct sem *sem_base; /* ptr to first semaphore in array */ struct sem_queue *sem_pending; /* pending operations to be processed */ struct sem_queue **sem_pending_last; /* last pending operation */ - struct sem_undo *undo; /* undo requests on this array */ + struct list_head list_id; /* undo requests on this array */ unsigned long sem_nsems; /* no. of semaphores in array */ }; @@ -118,8 +118,8 @@ struct sem_queue { * when the process exits. */ struct sem_undo { - struct sem_undo * proc_next; /* next entry on this process */ - struct sem_undo * id_next; /* next entry on this semaphore set */ + struct list_head list_proc; /* per-process list: all undos from one process */ + struct list_head list_id; /* per semaphore array list: all undos for one array */ int semid; /* semaphore set identifier */ short * semadj; /* array of adjustments, one per semaphore */ }; @@ -128,9 +128,9 @@ struct sem_undo { * that may be shared among all a CLONE_SYSVSEM task group. */ struct sem_undo_list { - atomic_t refcnt; - spinlock_t lock; - struct sem_undo *proc_list; + atomic_t refcnt; + spinlock_t lock; + struct list_head list_proc; }; struct sysv_sem { diff --git a/ipc/sem.c b/ipc/sem.c index e9418df5ff3e..4f26c7157356 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -274,7 +274,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) sma->sem_base = (struct sem *) &sma[1]; /* sma->sem_pending = NULL; */ sma->sem_pending_last = &sma->sem_pending; - /* sma->undo = NULL; */ + INIT_LIST_HEAD(&sma->list_id); sma->sem_nsems = nsems; sma->sem_ctime = get_seconds(); sem_unlock(sma); @@ -536,7 +536,8 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) * (They will be freed without any further action in exit_sem() * or during the next semop.) */ - for (un = sma->undo; un; un = un->id_next) + assert_spin_locked(&sma->sem_perm.lock); + list_for_each_entry(un, &sma->list_id, list_id) un->semid = -1; /* Wake up all pending processes and let them fail with EIDRM. */ @@ -763,9 +764,12 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, for (i = 0; i < nsems; i++) sma->sem_base[i].semval = sem_io[i]; - for (un = sma->undo; un; un = un->id_next) + + assert_spin_locked(&sma->sem_perm.lock); + list_for_each_entry(un, &sma->list_id, list_id) { for (i = 0; i < nsems; i++) un->semadj[i] = 0; + } sma->sem_ctime = get_seconds(); /* maybe some queued-up processes were waiting for this */ update_queue(sma); @@ -797,12 +801,15 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, { int val = arg.val; struct sem_undo *un; + err = -ERANGE; if (val > SEMVMX || val < 0) goto out_unlock; - for (un = sma->undo; un; un = un->id_next) + assert_spin_locked(&sma->sem_perm.lock); + list_for_each_entry(un, &sma->list_id, list_id) un->semadj[semnum] = 0; + curr->semval = val; curr->sempid = task_tgid_vnr(current); sma->sem_ctime = get_seconds(); @@ -952,6 +959,8 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp) return -ENOMEM; spin_lock_init(&undo_list->lock); atomic_set(&undo_list->refcnt, 1); + INIT_LIST_HEAD(&undo_list->list_proc); + current->sysvsem.undo_list = undo_list; } *undo_listp = undo_list; @@ -960,25 +969,30 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp) static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) { - struct sem_undo **last, *un; - - last = &ulp->proc_list; - un = *last; - while(un != NULL) { - if(un->semid==semid) - break; - if(un->semid==-1) { - *last=un->proc_next; - kfree(un); - } else { - last=&un->proc_next; + struct sem_undo *walk, *tmp; + + assert_spin_locked(&ulp->lock); + list_for_each_entry_safe(walk, tmp, &ulp->list_proc, list_proc) { + if (walk->semid == semid) + return walk; + if (walk->semid == -1) { + list_del(&walk->list_proc); + kfree(walk); } - un=*last; } - return un; + return NULL; } -static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) +/** + * find_alloc_undo - Lookup (and if not present create) undo array + * @ns: namespace + * @semid: semaphore array id + * + * The function looks up (and if not present creates) the undo structure. + * The size of the undo structure depends on the size of the semaphore + * array, thus the alloc path is not that straightforward. + */ +static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) { struct sem_array *sma; struct sem_undo_list *ulp; @@ -997,6 +1011,7 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) goto out; /* no undo structure around - allocate one. */ + /* step 1: figure out the size of the semaphore array */ sma = sem_lock_check(ns, semid); if (IS_ERR(sma)) return ERR_PTR(PTR_ERR(sma)); @@ -1004,15 +1019,19 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) nsems = sma->sem_nsems; sem_getref_and_unlock(sma); + /* step 2: allocate new undo structure */ new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL); if (!new) { sem_putref(sma); return ERR_PTR(-ENOMEM); } - new->semadj = (short *) &new[1]; - new->semid = semid; + /* step 3: Acquire the lock on the undo list pointer */ spin_lock(&ulp->lock); + + /* step 4: check for races: someone else allocated the undo struct, + * semaphore array was destroyed. + */ un = lookup_undo(ulp, semid); if (un) { spin_unlock(&ulp->lock); @@ -1028,13 +1047,17 @@ static struct sem_undo *find_undo(struct ipc_namespace *ns, int semid) un = ERR_PTR(-EIDRM); goto out; } - new->proc_next = ulp->proc_list; - ulp->proc_list = new; - new->id_next = sma->undo; - sma->undo = new; + /* step 5: initialize & link new undo structure */ + new->semadj = (short *) &new[1]; + new->semid = semid; + assert_spin_locked(&ulp->lock); + list_add(&new->list_proc, &ulp->list_proc); + assert_spin_locked(&sma->sem_perm.lock); + list_add(&new->list_id, &sma->list_id); + sem_unlock(sma); - un = new; spin_unlock(&ulp->lock); + un = new; out: return un; } @@ -1090,9 +1113,8 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, alter = 1; } -retry_undos: if (undos) { - un = find_undo(ns, semid); + un = find_alloc_undo(ns, semid); if (IS_ERR(un)) { error = PTR_ERR(un); goto out_free; @@ -1107,14 +1129,14 @@ retry_undos: } /* - * semid identifiers are not unique - find_undo may have + * semid identifiers are not unique - find_alloc_undo may have * allocated an undo structure, it was invalidated by an RMID - * and now a new array with received the same id. Check and retry. + * and now a new array with received the same id. Check and fail. */ - if (un && un->semid == -1) { - sem_unlock(sma); - goto retry_undos; - } + error = -EIDRM; + if (un && un->semid == -1) + goto out_unlock_free; + error = -EFBIG; if (max >= sma->sem_nsems) goto out_unlock_free; @@ -1243,56 +1265,44 @@ int copy_semundo(unsigned long clone_flags, struct task_struct *tsk) */ void exit_sem(struct task_struct *tsk) { - struct sem_undo_list *undo_list; - struct sem_undo *u, **up; - struct ipc_namespace *ns; + struct sem_undo_list *ulp; + struct sem_undo *un, *tmp; - undo_list = tsk->sysvsem.undo_list; - if (!undo_list) + ulp = tsk->sysvsem.undo_list; + if (!ulp) return; tsk->sysvsem.undo_list = NULL; - if (!atomic_dec_and_test(&undo_list->refcnt)) + if (!atomic_dec_and_test(&ulp->refcnt)) return; - ns = tsk->nsproxy->ipc_ns; - /* There's no need to hold the semundo list lock, as current - * is the last task exiting for this undo list. - */ - for (up = &undo_list->proc_list; (u = *up); *up = u->proc_next, kfree(u)) { + spin_lock(&ulp->lock); + + list_for_each_entry_safe(un, tmp, &ulp->list_proc, list_proc) { struct sem_array *sma; - int nsems, i; - struct sem_undo *un, **unp; - int semid; - - semid = u->semid; - - if(semid == -1) - continue; - sma = sem_lock(ns, semid); + int i; + + if (un->semid == -1) + goto free; + + sma = sem_lock(tsk->nsproxy->ipc_ns, un->semid); if (IS_ERR(sma)) - continue; + goto free; - if (u->semid == -1) - goto next_entry; + if (un->semid == -1) + goto unlock_free; - BUG_ON(sem_checkid(sma, u->semid)); + BUG_ON(sem_checkid(sma, un->semid)); - /* remove u from the sma->undo list */ - for (unp = &sma->undo; (un = *unp); unp = &un->id_next) { - if (u == un) - goto found; - } - printk ("exit_sem undo list error id=%d\n", u->semid); - goto next_entry; -found: - *unp = un->id_next; - /* perform adjustments registered in u */ - nsems = sma->sem_nsems; - for (i = 0; i < nsems; i++) { + /* remove un from sma->list_id */ + assert_spin_locked(&sma->sem_perm.lock); + list_del(&un->list_id); + + /* perform adjustments registered in un */ + for (i = 0; i < sma->sem_nsems; i++) { struct sem * semaphore = &sma->sem_base[i]; - if (u->semadj[i]) { - semaphore->semval += u->semadj[i]; + if (un->semadj[i]) { + semaphore->semval += un->semadj[i]; /* * Range checks of the new semaphore value, * not defined by sus: @@ -1316,10 +1326,15 @@ found: sma->sem_otime = get_seconds(); /* maybe some queued-up processes were waiting for this */ update_queue(sma); -next_entry: +unlock_free: sem_unlock(sma); +free: + assert_spin_locked(&ulp->lock); + list_del(&un->list_proc); + kfree(un); } - kfree(undo_list); + spin_unlock(&ulp->lock); + kfree(ulp); } #ifdef CONFIG_PROC_FS -- cgit From 2c0c29d414087f3b021059673c20a7088f5f1fff Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Fri, 25 Jul 2008 01:48:05 -0700 Subject: ipc/sem.c: remove unused entries from struct sem_queue sem_queue.sma and sem_queue.id were never used, the attached patch removes them. Signed-off-by: Manfred Spraul Reviewed-by: Nadia Derbey Cc: Pierre Peiffer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sem.h | 2 -- ipc/sem.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sem.h b/include/linux/sem.h index 6a1af1b49a13..87756ef1198e 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -107,8 +107,6 @@ struct sem_queue { struct sem_undo * undo; /* undo structure */ int pid; /* process id of requesting process */ int status; /* completion status of operation */ - struct sem_array * sma; /* semaphore array for operations */ - int id; /* internal sem id */ struct sembuf * sops; /* array of pending operations */ int nsops; /* number of operations */ int alter; /* does the operation alter the array? */ diff --git a/ipc/sem.c b/ipc/sem.c index 4f26c7157356..d5ce4000ca17 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1160,12 +1160,10 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, * task into the pending queue and go to sleep. */ - queue.sma = sma; queue.sops = sops; queue.nsops = nsops; queue.undo = un; queue.pid = task_tgid_vnr(current); - queue.id = semid; queue.alter = alter; if (alter) append_to_queue(sma ,&queue); -- cgit From a1193f8ec091cd8fd309cc2982abe4499f6f2b4d Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Fri, 25 Jul 2008 01:48:06 -0700 Subject: ipc/sem.c: convert sem_array.sem_pending to struct list_head sem_array.sem_pending is a double linked list, the attached patch converts it to struct list_head. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Manfred Spraul Reviewed-by: Nadia Derbey Cc: Pierre Peiffer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sem.h | 12 +++---- ipc/sem.c | 92 ++++++++++++++++++++--------------------------------- 2 files changed, 40 insertions(+), 64 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sem.h b/include/linux/sem.h index 87756ef1198e..d42599395d79 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -93,21 +93,19 @@ struct sem_array { time_t sem_otime; /* last semop time */ time_t sem_ctime; /* last change time */ struct sem *sem_base; /* ptr to first semaphore in array */ - struct sem_queue *sem_pending; /* pending operations to be processed */ - struct sem_queue **sem_pending_last; /* last pending operation */ + struct list_head sem_pending; /* pending operations to be processed */ struct list_head list_id; /* undo requests on this array */ unsigned long sem_nsems; /* no. of semaphores in array */ }; /* One queue for each sleeping process in the system. */ struct sem_queue { - struct sem_queue * next; /* next entry in the queue */ - struct sem_queue ** prev; /* previous entry in the queue, *(q->prev) == q */ - struct task_struct* sleeper; /* this process */ - struct sem_undo * undo; /* undo structure */ + struct list_head list; /* queue of pending operations */ + struct task_struct *sleeper; /* this process */ + struct sem_undo *undo; /* undo structure */ int pid; /* process id of requesting process */ int status; /* completion status of operation */ - struct sembuf * sops; /* array of pending operations */ + struct sembuf *sops; /* array of pending operations */ int nsops; /* number of operations */ int alter; /* does the operation alter the array? */ }; diff --git a/ipc/sem.c b/ipc/sem.c index d5ce4000ca17..3ca232736b31 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -272,8 +272,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) ns->used_sems += nsems; sma->sem_base = (struct sem *) &sma[1]; - /* sma->sem_pending = NULL; */ - sma->sem_pending_last = &sma->sem_pending; + INIT_LIST_HEAD(&sma->sem_pending); INIT_LIST_HEAD(&sma->list_id); sma->sem_nsems = nsems; sma->sem_ctime = get_seconds(); @@ -331,38 +330,6 @@ asmlinkage long sys_semget(key_t key, int nsems, int semflg) return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); } -/* Manage the doubly linked list sma->sem_pending as a FIFO: - * insert new queue elements at the tail sma->sem_pending_last. - */ -static inline void append_to_queue (struct sem_array * sma, - struct sem_queue * q) -{ - *(q->prev = sma->sem_pending_last) = q; - *(sma->sem_pending_last = &q->next) = NULL; -} - -static inline void prepend_to_queue (struct sem_array * sma, - struct sem_queue * q) -{ - q->next = sma->sem_pending; - *(q->prev = &sma->sem_pending) = q; - if (q->next) - q->next->prev = &q->next; - else /* sma->sem_pending_last == &sma->sem_pending */ - sma->sem_pending_last = &q->next; -} - -static inline void remove_from_queue (struct sem_array * sma, - struct sem_queue * q) -{ - *(q->prev) = q->next; - if (q->next) - q->next->prev = q->prev; - else /* sma->sem_pending_last == &q->next */ - sma->sem_pending_last = q->prev; - q->prev = NULL; /* mark as removed */ -} - /* * Determine whether a sequence of semaphore operations would succeed * all at once. Return 0 if yes, 1 if need to sleep, else return error code. @@ -438,16 +405,15 @@ static void update_queue (struct sem_array * sma) int error; struct sem_queue * q; - q = sma->sem_pending; - while(q) { + q = list_entry(sma->sem_pending.next, struct sem_queue, list); + while (&q->list != &sma->sem_pending) { error = try_atomic_semop(sma, q->sops, q->nsops, q->undo, q->pid); /* Does q->sleeper still need to sleep? */ if (error <= 0) { struct sem_queue *n; - remove_from_queue(sma,q); - q->status = IN_WAKEUP; + /* * Continue scanning. The next operation * that must be checked depends on the type of the @@ -458,11 +424,26 @@ static void update_queue (struct sem_array * sma) * for semaphore values to become 0. * - if the operation didn't modify the array, * then just continue. + * The order of list_del() and reading ->next + * is crucial: In the former case, the list_del() + * must be done first [because we might be the + * first entry in ->sem_pending], in the latter + * case the list_del() must be done last + * [because the list is invalid after the list_del()] */ - if (q->alter) - n = sma->sem_pending; - else - n = q->next; + if (q->alter) { + list_del(&q->list); + n = list_entry(sma->sem_pending.next, + struct sem_queue, list); + } else { + n = list_entry(q->list.next, struct sem_queue, + list); + list_del(&q->list); + } + + /* wake up the waiting thread */ + q->status = IN_WAKEUP; + wake_up_process(q->sleeper); /* hands-off: q will disappear immediately after * writing q->status. @@ -471,7 +452,7 @@ static void update_queue (struct sem_array * sma) q->status = error; q = n; } else { - q = q->next; + q = list_entry(q->list.next, struct sem_queue, list); } } } @@ -491,7 +472,7 @@ static int count_semncnt (struct sem_array * sma, ushort semnum) struct sem_queue * q; semncnt = 0; - for (q = sma->sem_pending; q; q = q->next) { + list_for_each_entry(q, &sma->sem_pending, list) { struct sembuf * sops = q->sops; int nsops = q->nsops; int i; @@ -503,13 +484,14 @@ static int count_semncnt (struct sem_array * sma, ushort semnum) } return semncnt; } + static int count_semzcnt (struct sem_array * sma, ushort semnum) { int semzcnt; struct sem_queue * q; semzcnt = 0; - for (q = sma->sem_pending; q; q = q->next) { + list_for_each_entry(q, &sma->sem_pending, list) { struct sembuf * sops = q->sops; int nsops = q->nsops; int i; @@ -529,7 +511,7 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) { struct sem_undo *un; - struct sem_queue *q; + struct sem_queue *q, *t; struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); /* Invalidate the existing undo structures for this semaphore set. @@ -541,17 +523,14 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) un->semid = -1; /* Wake up all pending processes and let them fail with EIDRM. */ - q = sma->sem_pending; - while(q) { - struct sem_queue *n; - /* lazy remove_from_queue: we are killing the whole queue */ - q->prev = NULL; - n = q->next; + + list_for_each_entry_safe(q, t, &sma->sem_pending, list) { + list_del(&q->list); + q->status = IN_WAKEUP; wake_up_process(q->sleeper); /* doesn't sleep */ smp_wmb(); q->status = -EIDRM; /* hands-off q */ - q = n; } /* Remove the semaphore set from the IDR */ @@ -1166,9 +1145,9 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, queue.pid = task_tgid_vnr(current); queue.alter = alter; if (alter) - append_to_queue(sma ,&queue); + list_add_tail(&queue.list, &sma->sem_pending); else - prepend_to_queue(sma ,&queue); + list_add(&queue.list, &sma->sem_pending); queue.status = -EINTR; queue.sleeper = current; @@ -1194,7 +1173,6 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, sma = sem_lock(ns, semid); if (IS_ERR(sma)) { - BUG_ON(queue.prev != NULL); error = -EIDRM; goto out_free; } @@ -1212,7 +1190,7 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, */ if (timeout && jiffies_left == 0) error = -EAGAIN; - remove_from_queue(sma,&queue); + list_del(&queue.list); goto out_unlock_free; out_unlock_free: -- cgit From 380af1b33b3ff92df5cda96329b58f5d1b6b5a53 Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Fri, 25 Jul 2008 01:48:06 -0700 Subject: ipc/sem.c: rewrite undo list locking The attached patch: - reverses the locking order of ulp->lock and sem_lock: Previously, it was first ulp->lock, then inside sem_lock. Now it's the other way around. - converts the undo structure to rcu. Benefits: - With the old locking order, IPC_RMID could not kfree the undo structures. The stale entries remained in the linked lists and were released later. - The patch fixes a a race in semtimedop(): if both IPC_RMID and a semget() that recreates exactly the same id happen between find_alloc_undo() and sem_lock, then semtimedop() would access already kfree'd memory. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Manfred Spraul Reviewed-by: Nadia Derbey Cc: Pierre Peiffer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sem.h | 6 ++- ipc/sem.c | 147 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 98 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sem.h b/include/linux/sem.h index d42599395d79..1b191c176bcd 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -78,6 +78,7 @@ struct seminfo { #ifdef __KERNEL__ #include +#include struct task_struct; @@ -114,7 +115,10 @@ struct sem_queue { * when the process exits. */ struct sem_undo { - struct list_head list_proc; /* per-process list: all undos from one process */ + struct list_head list_proc; /* per-process list: all undos from one process. */ + /* rcu protected */ + struct rcu_head rcu; /* rcu struct for sem_undo() */ + struct sem_undo_list *ulp; /* sem_undo_list for the process */ struct list_head list_id; /* per semaphore array list: all undos for one array */ int semid; /* semaphore set identifier */ short * semadj; /* array of adjustments, one per semaphore */ diff --git a/ipc/sem.c b/ipc/sem.c index 3ca232736b31..bf1bc36cb7ee 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -504,27 +504,35 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) return semzcnt; } +void free_un(struct rcu_head *head) +{ + struct sem_undo *un = container_of(head, struct sem_undo, rcu); + kfree(un); +} + /* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex * remains locked on exit. */ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) { - struct sem_undo *un; - struct sem_queue *q, *t; + struct sem_undo *un, *tu; + struct sem_queue *q, *tq; struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); - /* Invalidate the existing undo structures for this semaphore set. - * (They will be freed without any further action in exit_sem() - * or during the next semop.) - */ + /* Free the existing undo structures for this semaphore set. */ assert_spin_locked(&sma->sem_perm.lock); - list_for_each_entry(un, &sma->list_id, list_id) + list_for_each_entry_safe(un, tu, &sma->list_id, list_id) { + list_del(&un->list_id); + spin_lock(&un->ulp->lock); un->semid = -1; + list_del_rcu(&un->list_proc); + spin_unlock(&un->ulp->lock); + call_rcu(&un->rcu, free_un); + } /* Wake up all pending processes and let them fail with EIDRM. */ - - list_for_each_entry_safe(q, t, &sma->sem_pending, list) { + list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { list_del(&q->list); q->status = IN_WAKEUP; @@ -948,16 +956,11 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp) static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) { - struct sem_undo *walk, *tmp; + struct sem_undo *walk; - assert_spin_locked(&ulp->lock); - list_for_each_entry_safe(walk, tmp, &ulp->list_proc, list_proc) { + list_for_each_entry_rcu(walk, &ulp->list_proc, list_proc) { if (walk->semid == semid) return walk; - if (walk->semid == -1) { - list_del(&walk->list_proc); - kfree(walk); - } } return NULL; } @@ -970,6 +973,8 @@ static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) * The function looks up (and if not present creates) the undo structure. * The size of the undo structure depends on the size of the semaphore * array, thus the alloc path is not that straightforward. + * Lifetime-rules: sem_undo is rcu-protected, on success, the function + * performs a rcu_read_lock(). */ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) { @@ -983,11 +988,13 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) if (error) return ERR_PTR(error); + rcu_read_lock(); spin_lock(&ulp->lock); un = lookup_undo(ulp, semid); spin_unlock(&ulp->lock); if (likely(un!=NULL)) goto out; + rcu_read_unlock(); /* no undo structure around - allocate one. */ /* step 1: figure out the size of the semaphore array */ @@ -1005,38 +1012,38 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) return ERR_PTR(-ENOMEM); } - /* step 3: Acquire the lock on the undo list pointer */ - spin_lock(&ulp->lock); - - /* step 4: check for races: someone else allocated the undo struct, - * semaphore array was destroyed. - */ - un = lookup_undo(ulp, semid); - if (un) { - spin_unlock(&ulp->lock); - kfree(new); - sem_putref(sma); - goto out; - } + /* step 3: Acquire the lock on semaphore array */ sem_lock_and_putref(sma); if (sma->sem_perm.deleted) { sem_unlock(sma); - spin_unlock(&ulp->lock); kfree(new); un = ERR_PTR(-EIDRM); goto out; } + spin_lock(&ulp->lock); + + /* + * step 4: check for races: did someone else allocate the undo struct? + */ + un = lookup_undo(ulp, semid); + if (un) { + kfree(new); + goto success; + } /* step 5: initialize & link new undo structure */ new->semadj = (short *) &new[1]; + new->ulp = ulp; new->semid = semid; assert_spin_locked(&ulp->lock); - list_add(&new->list_proc, &ulp->list_proc); + list_add_rcu(&new->list_proc, &ulp->list_proc); assert_spin_locked(&sma->sem_perm.lock); list_add(&new->list_id, &sma->list_id); + un = new; - sem_unlock(sma); +success: spin_unlock(&ulp->lock); - un = new; + rcu_read_lock(); + sem_unlock(sma); out: return un; } @@ -1103,6 +1110,8 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, sma = sem_lock_check(ns, semid); if (IS_ERR(sma)) { + if (un) + rcu_read_unlock(); error = PTR_ERR(sma); goto out_free; } @@ -1111,10 +1120,26 @@ asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops, * semid identifiers are not unique - find_alloc_undo may have * allocated an undo structure, it was invalidated by an RMID * and now a new array with received the same id. Check and fail. + * This case can be detected checking un->semid. The existance of + * "un" itself is guaranteed by rcu. */ error = -EIDRM; - if (un && un->semid == -1) - goto out_unlock_free; + if (un) { + if (un->semid == -1) { + rcu_read_unlock(); + goto out_unlock_free; + } else { + /* + * rcu lock can be released, "un" cannot disappear: + * - sem_lock is acquired, thus IPC_RMID is + * impossible. + * - exit_sem is impossible, it always operates on + * current (or a dead task). + */ + + rcu_read_unlock(); + } + } error = -EFBIG; if (max >= sma->sem_nsems) @@ -1242,7 +1267,6 @@ int copy_semundo(unsigned long clone_flags, struct task_struct *tsk) void exit_sem(struct task_struct *tsk) { struct sem_undo_list *ulp; - struct sem_undo *un, *tmp; ulp = tsk->sysvsem.undo_list; if (!ulp) @@ -1252,28 +1276,47 @@ void exit_sem(struct task_struct *tsk) if (!atomic_dec_and_test(&ulp->refcnt)) return; - spin_lock(&ulp->lock); - - list_for_each_entry_safe(un, tmp, &ulp->list_proc, list_proc) { + for (;;) { struct sem_array *sma; + struct sem_undo *un; + int semid; int i; - if (un->semid == -1) - goto free; + rcu_read_lock(); + un = list_entry(rcu_dereference(ulp->list_proc.next), + struct sem_undo, list_proc); + if (&un->list_proc == &ulp->list_proc) + semid = -1; + else + semid = un->semid; + rcu_read_unlock(); - sma = sem_lock(tsk->nsproxy->ipc_ns, un->semid); - if (IS_ERR(sma)) - goto free; + if (semid == -1) + break; - if (un->semid == -1) - goto unlock_free; + sma = sem_lock_check(tsk->nsproxy->ipc_ns, un->semid); - BUG_ON(sem_checkid(sma, un->semid)); + /* exit_sem raced with IPC_RMID, nothing to do */ + if (IS_ERR(sma)) + continue; - /* remove un from sma->list_id */ + un = lookup_undo(ulp, semid); + if (un == NULL) { + /* exit_sem raced with IPC_RMID+semget() that created + * exactly the same semid. Nothing to do. + */ + sem_unlock(sma); + continue; + } + + /* remove un from the linked lists */ assert_spin_locked(&sma->sem_perm.lock); list_del(&un->list_id); + spin_lock(&ulp->lock); + list_del_rcu(&un->list_proc); + spin_unlock(&ulp->lock); + /* perform adjustments registered in un */ for (i = 0; i < sma->sem_nsems; i++) { struct sem * semaphore = &sma->sem_base[i]; @@ -1302,14 +1345,10 @@ void exit_sem(struct task_struct *tsk) sma->sem_otime = get_seconds(); /* maybe some queued-up processes were waiting for this */ update_queue(sma); -unlock_free: sem_unlock(sma); -free: - assert_spin_locked(&ulp->lock); - list_del(&un->list_proc); - kfree(un); + + call_rcu(&un->rcu, free_un); } - spin_unlock(&ulp->lock); kfree(ulp); } -- cgit From 9eefe520c814f6f62c5d36a2ddcd3fb99dfdb30e Mon Sep 17 00:00:00 2001 From: Nadia Derbey Date: Fri, 25 Jul 2008 01:48:08 -0700 Subject: ipc: do not use a negative value to re-enable msgmni automatic recomputing This patch proposes an alternative to the "magical positive-versus-negative number trick" Andrew complained about last week in http://lkml.org/lkml/2008/6/24/418. This had been introduced with the patches that scale msgmni to the amount of lowmem. With these patches, msgmni has a registered notification routine that recomputes msgmni value upon memory add/remove or ipc namespace creation/ removal. When msgmni is changed from user space (i.e. value written to the proc file), that notification routine is unregistered, and the way to make it registered back is to write a negative value into the proc file. This is the "magical positive-versus-negative number trick". To fix this, a new proc file is introduced: /proc/sys/kernel/auto_msgmni. This file acts as ON/OFF for msgmni automatic recomputing. With this patch, the process is the following: 1) kernel boots in "automatic recomputing mode" /proc/sys/kernel/msgmni contains the value that has been computed (depends on lowmem) /proc/sys/kernel/automatic_msgmni contains "1" 2) echo > /proc/sys/kernel/msgmni . sets msg_ctlmni to . de-activates automatic recomputing (i.e. if, say, some memory is added msgmni won't be recomputed anymore) . /proc/sys/kernel/automatic_msgmni now contains "0" 3) echo "0" > /proc/sys/kernel/automatic_msgmni . de-activates msgmni automatic recomputing this has the same effect as 2) except that msg_ctlmni's value stays blocked at its current value) 3) echo "1" > /proc/sys/kernel/automatic_msgmni . recomputes msgmni's value based on the current available memory size and number of ipc namespaces . re-activates automatic recomputing for msgmni. Signed-off-by: Nadia Derbey Cc: Solofo Ramangalahy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ipc_namespace.h | 3 +- ipc/ipc_sysctl.c | 72 +++++++++++++++++++++++++++++++++++-------- ipc/ipcns_notifier.c | 20 +++++++++--- 3 files changed, 76 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h index ea6c18a8b0d4..ea330f9e7100 100644 --- a/include/linux/ipc_namespace.h +++ b/include/linux/ipc_namespace.h @@ -36,6 +36,7 @@ struct ipc_namespace { int msg_ctlmni; atomic_t msg_bytes; atomic_t msg_hdrs; + int auto_msgmni; size_t shm_ctlmax; size_t shm_ctlall; @@ -53,7 +54,7 @@ extern atomic_t nr_ipc_ns; extern int register_ipcns_notifier(struct ipc_namespace *); extern int cond_register_ipcns_notifier(struct ipc_namespace *); -extern int unregister_ipcns_notifier(struct ipc_namespace *); +extern void unregister_ipcns_notifier(struct ipc_namespace *); extern int ipcns_notify(unsigned long); #else /* CONFIG_SYSVIPC */ diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c index d3497465cc0a..69bc85978ba0 100644 --- a/ipc/ipc_sysctl.c +++ b/ipc/ipc_sysctl.c @@ -27,15 +27,17 @@ static void *get_ipc(ctl_table *table) } /* - * Routine that is called when a tunable has successfully been changed by - * hand and it has a callback routine registered on the ipc namespace notifier - * chain: we don't want such tunables to be recomputed anymore upon memory - * add/remove or ipc namespace creation/removal. - * They can come back to a recomputable state by being set to a <0 value. + * Routine that is called when the file "auto_msgmni" has successfully been + * written. + * Two values are allowed: + * 0: unregister msgmni's callback routine from the ipc namespace notifier + * chain. This means that msgmni won't be recomputed anymore upon memory + * add/remove or ipc namespace creation/removal. + * 1: register back the callback routine. */ -static void tunable_set_callback(int val) +static void ipc_auto_callback(int val) { - if (val >= 0) + if (!val) unregister_ipcns_notifier(current->nsproxy->ipc_ns); else { /* @@ -71,7 +73,12 @@ static int proc_ipc_callback_dointvec(ctl_table *table, int write, rc = proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos); if (write && !rc && lenp_bef == *lenp) - tunable_set_callback(*((int *)(ipc_table.data))); + /* + * Tunable has successfully been changed by hand. Disable its + * automatic adjustment. This simply requires unregistering + * the notifiers that trigger recalculation. + */ + unregister_ipcns_notifier(current->nsproxy->ipc_ns); return rc; } @@ -87,10 +94,39 @@ static int proc_ipc_doulongvec_minmax(ctl_table *table, int write, lenp, ppos); } +static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write, + struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) +{ + struct ctl_table ipc_table; + size_t lenp_bef = *lenp; + int oldval; + int rc; + + memcpy(&ipc_table, table, sizeof(ipc_table)); + ipc_table.data = get_ipc(table); + oldval = *((int *)(ipc_table.data)); + + rc = proc_dointvec_minmax(&ipc_table, write, filp, buffer, lenp, ppos); + + if (write && !rc && lenp_bef == *lenp) { + int newval = *((int *)(ipc_table.data)); + /* + * The file "auto_msgmni" has correctly been set. + * React by (un)registering the corresponding tunable, if the + * value has changed. + */ + if (newval != oldval) + ipc_auto_callback(newval); + } + + return rc; +} + #else #define proc_ipc_doulongvec_minmax NULL #define proc_ipc_dointvec NULL #define proc_ipc_callback_dointvec NULL +#define proc_ipcauto_dointvec_minmax NULL #endif #ifdef CONFIG_SYSCTL_SYSCALL @@ -142,14 +178,11 @@ static int sysctl_ipc_registered_data(ctl_table *table, int __user *name, rc = sysctl_ipc_data(table, name, nlen, oldval, oldlenp, newval, newlen); - if (newval && newlen && rc > 0) { + if (newval && newlen && rc > 0) /* * Tunable has successfully been changed from userland */ - int *data = get_ipc(table); - - tunable_set_callback(*data); - } + unregister_ipcns_notifier(current->nsproxy->ipc_ns); return rc; } @@ -158,6 +191,9 @@ static int sysctl_ipc_registered_data(ctl_table *table, int __user *name, #define sysctl_ipc_registered_data NULL #endif +static int zero; +static int one = 1; + static struct ctl_table ipc_kern_table[] = { { .ctl_name = KERN_SHMMAX, @@ -222,6 +258,16 @@ static struct ctl_table ipc_kern_table[] = { .proc_handler = proc_ipc_dointvec, .strategy = sysctl_ipc_data, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "auto_msgmni", + .data = &init_ipc_ns.auto_msgmni, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_ipcauto_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, {} }; diff --git a/ipc/ipcns_notifier.c b/ipc/ipcns_notifier.c index 70ff09183f7b..b9b31a4f77e1 100644 --- a/ipc/ipcns_notifier.c +++ b/ipc/ipcns_notifier.c @@ -55,25 +55,35 @@ static int ipcns_callback(struct notifier_block *self, int register_ipcns_notifier(struct ipc_namespace *ns) { + int rc; + memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); ns->ipcns_nb.notifier_call = ipcns_callback; ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; - return blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb); + rc = blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb); + if (!rc) + ns->auto_msgmni = 1; + return rc; } int cond_register_ipcns_notifier(struct ipc_namespace *ns) { + int rc; + memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); ns->ipcns_nb.notifier_call = ipcns_callback; ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; - return blocking_notifier_chain_cond_register(&ipcns_chain, + rc = blocking_notifier_chain_cond_register(&ipcns_chain, &ns->ipcns_nb); + if (!rc) + ns->auto_msgmni = 1; + return rc; } -int unregister_ipcns_notifier(struct ipc_namespace *ns) +void unregister_ipcns_notifier(struct ipc_namespace *ns) { - return blocking_notifier_chain_unregister(&ipcns_chain, - &ns->ipcns_nb); + blocking_notifier_chain_unregister(&ipcns_chain, &ns->ipcns_nb); + ns->auto_msgmni = 0; } int ipcns_notify(unsigned long val) -- cgit From d805dda412346225a50af2d399d958a4bc676c38 Mon Sep 17 00:00:00 2001 From: Abdel Benamrouche Date: Fri, 25 Jul 2008 01:48:25 -0700 Subject: fs/partition/check.c: fix return value warning fs/partitions/check.c:381: warning: ignoring return value of ___device_add___, declared with attribute warn_unused_result [akpm@linux-foundation.org: multiple-return-statements-per-function are evil] Signed-off-by: Abdel Benamrouche Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/partitions/check.c | 28 ++++++++++++++++++++++------ include/linux/genhd.h | 2 +- 2 files changed, 23 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/fs/partitions/check.c b/fs/partitions/check.c index efef715135d3..2e6413fbd2d8 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -344,18 +344,18 @@ static ssize_t whole_disk_show(struct device *dev, static DEVICE_ATTR(whole_disk, S_IRUSR | S_IRGRP | S_IROTH, whole_disk_show, NULL); -void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags) +int add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags) { struct hd_struct *p; int err; p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) - return; + return -ENOMEM; if (!init_part_stats(p)) { - kfree(p); - return; + err = -ENOMEM; + goto out0; } p->start_sect = start; p->nr_sects = len; @@ -378,15 +378,31 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, /* delay uevent until 'holders' subdir is created */ p->dev.uevent_suppress = 1; - device_add(&p->dev); + err = device_add(&p->dev); + if (err) + goto out1; partition_sysfs_add_subdir(p); p->dev.uevent_suppress = 0; - if (flags & ADDPART_FLAG_WHOLEDISK) + if (flags & ADDPART_FLAG_WHOLEDISK) { err = device_create_file(&p->dev, &dev_attr_whole_disk); + if (err) + goto out2; + } /* suppress uevent if the disk supresses it */ if (!disk->dev.uevent_suppress) kobject_uevent(&p->dev.kobj, KOBJ_ADD); + + return 0; + +out2: + device_del(&p->dev); +out1: + put_device(&p->dev); + free_part_stats(p); +out0: + kfree(p); + return err; } /* Not exported, helper to add_disk(). */ diff --git a/include/linux/genhd.h b/include/linux/genhd.h index e8787417f65a..118216f1bd3c 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -541,7 +541,7 @@ extern dev_t blk_lookup_devt(const char *name, int part); extern char *disk_name (struct gendisk *hd, int part, char *buf); extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev); -extern void add_partition(struct gendisk *, int, sector_t, sector_t, int); +extern int __must_check add_partition(struct gendisk *, int, sector_t, sector_t, int); extern void delete_partition(struct gendisk *, int); extern void printk_all_partitions(void); -- cgit From 6e644c3126149b65460610fe5a00d8a162092abe Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:48:28 -0700 Subject: move proc_kmsg_operations to fs/proc/internal.h This patch moves the extern of struct proc_kmsg_operations to fs/proc/internal.h and adds an #include "internal.h" to fs/proc/kmsg.c so that the latter sees the former. Signed-off-by: Adrian Bunk Cc: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/internal.h | 1 + fs/proc/kmsg.c | 2 ++ include/linux/proc_fs.h | 1 - 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 28cbca805905..8d67616e7bb0 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -63,6 +63,7 @@ extern const struct file_operations proc_smaps_operations; extern const struct file_operations proc_clear_refs_operations; extern const struct file_operations proc_pagemap_operations; extern const struct file_operations proc_net_operations; +extern const struct file_operations proc_kmsg_operations; extern const struct inode_operations proc_net_inode_operations; void free_proc_entry(struct proc_dir_entry *de); diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index ff3b90b56e9d..9fd5df3f40ce 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -15,6 +15,8 @@ #include #include +#include "internal.h" + extern wait_queue_head_t log_wait; extern int do_syslog(int type, char __user *bug, int count); diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 15a9eaf4a802..cdabc2fc02f7 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -138,7 +138,6 @@ extern int proc_readdir(struct file *, void *, filldir_t); extern struct dentry *proc_lookup(struct inode *, struct dentry *, struct nameidata *); extern const struct file_operations proc_kcore_operations; -extern const struct file_operations proc_kmsg_operations; extern const struct file_operations ppc_htab_operations; extern int pid_ns_prepare_proc(struct pid_namespace *ns); -- cgit From 881adb85358309ea9c6f707394002719982ec607 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 25 Jul 2008 01:48:29 -0700 Subject: proc: always do ->release Current two-stage scheme of removing PDE emphasizes one bug in proc: open rmmod remove_proc_entry close ->release won't be called because ->proc_fops were cleared. In simple cases it's small memory leak. For every ->open, ->release has to be done. List of openers is introduced which is traversed at remove_proc_entry() if neeeded. Discussions with Al long ago (sigh). Signed-off-by: Alexey Dobriyan Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/generic.c | 14 ++++++++++ fs/proc/inode.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++--- fs/proc/internal.h | 7 +++++ include/linux/proc_fs.h | 1 + 4 files changed, 92 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 43e54e86cefd..bc0a0dd2d844 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -597,6 +597,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent, ent->pde_users = 0; spin_lock_init(&ent->pde_unload_lock); ent->pde_unload_completion = NULL; + INIT_LIST_HEAD(&ent->pde_openers); out: return ent; } @@ -789,6 +790,19 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) spin_unlock(&de->pde_unload_lock); continue_removing: + spin_lock(&de->pde_unload_lock); + while (!list_empty(&de->pde_openers)) { + struct pde_opener *pdeo; + + pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh); + list_del(&pdeo->lh); + spin_unlock(&de->pde_unload_lock); + pdeo->release(pdeo->inode, pdeo->file); + kfree(pdeo); + spin_lock(&de->pde_unload_lock); + } + spin_unlock(&de->pde_unload_lock); + if (S_ISDIR(de->mode)) parent->nlink--; de->nlink = 0; diff --git a/fs/proc/inode.c b/fs/proc/inode.c index b08d10017911..354c08485825 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -126,12 +126,17 @@ static const struct super_operations proc_sops = { .remount_fs = proc_remount, }; -static void pde_users_dec(struct proc_dir_entry *pde) +static void __pde_users_dec(struct proc_dir_entry *pde) { - spin_lock(&pde->pde_unload_lock); pde->pde_users--; if (pde->pde_unload_completion && pde->pde_users == 0) complete(pde->pde_unload_completion); +} + +static void pde_users_dec(struct proc_dir_entry *pde) +{ + spin_lock(&pde->pde_unload_lock); + __pde_users_dec(pde); spin_unlock(&pde->pde_unload_lock); } @@ -318,36 +323,97 @@ static int proc_reg_open(struct inode *inode, struct file *file) struct proc_dir_entry *pde = PDE(inode); int rv = 0; int (*open)(struct inode *, struct file *); + int (*release)(struct inode *, struct file *); + struct pde_opener *pdeo; + + /* + * What for, you ask? Well, we can have open, rmmod, remove_proc_entry + * sequence. ->release won't be called because ->proc_fops will be + * cleared. Depending on complexity of ->release, consequences vary. + * + * We can't wait for mercy when close will be done for real, it's + * deadlockable: rmmod foo release + * by hand in remove_proc_entry(). For this, save opener's credentials + * for later. + */ + pdeo = kmalloc(sizeof(struct pde_opener), GFP_KERNEL); + if (!pdeo) + return -ENOMEM; spin_lock(&pde->pde_unload_lock); if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); + kfree(pdeo); return rv; } pde->pde_users++; open = pde->proc_fops->open; + release = pde->proc_fops->release; spin_unlock(&pde->pde_unload_lock); if (open) rv = open(inode, file); - pde_users_dec(pde); + spin_lock(&pde->pde_unload_lock); + if (rv == 0 && release) { + /* To know what to release. */ + pdeo->inode = inode; + pdeo->file = file; + /* Strictly for "too late" ->release in proc_reg_release(). */ + pdeo->release = release; + list_add(&pdeo->lh, &pde->pde_openers); + } else + kfree(pdeo); + __pde_users_dec(pde); + spin_unlock(&pde->pde_unload_lock); return rv; } +static struct pde_opener *find_pde_opener(struct proc_dir_entry *pde, + struct inode *inode, struct file *file) +{ + struct pde_opener *pdeo; + + list_for_each_entry(pdeo, &pde->pde_openers, lh) { + if (pdeo->inode == inode && pdeo->file == file) + return pdeo; + } + return NULL; +} + static int proc_reg_release(struct inode *inode, struct file *file) { struct proc_dir_entry *pde = PDE(inode); int rv = 0; int (*release)(struct inode *, struct file *); + struct pde_opener *pdeo; spin_lock(&pde->pde_unload_lock); + pdeo = find_pde_opener(pde, inode, file); if (!pde->proc_fops) { - spin_unlock(&pde->pde_unload_lock); + /* + * Can't simply exit, __fput() will think that everything is OK, + * and move on to freeing struct file. remove_proc_entry() will + * find slacker in opener's list and will try to do non-trivial + * things with struct file. Therefore, remove opener from list. + * + * But if opener is removed from list, who will ->release it? + */ + if (pdeo) { + list_del(&pdeo->lh); + spin_unlock(&pde->pde_unload_lock); + rv = pdeo->release(inode, file); + kfree(pdeo); + } else + spin_unlock(&pde->pde_unload_lock); return rv; } pde->pde_users++; release = pde->proc_fops->release; + if (pdeo) { + list_del(&pdeo->lh); + kfree(pdeo); + } spin_unlock(&pde->pde_unload_lock); if (release) diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 8d67616e7bb0..442202314d53 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -89,3 +89,10 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *ino, struct dentry *dentry); int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent, filldir_t filldir); + +struct pde_opener { + struct inode *inode; + struct file *file; + int (*release)(struct inode *, struct file *); + struct list_head lh; +}; diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index cdabc2fc02f7..f560d1705afe 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -79,6 +79,7 @@ struct proc_dir_entry { int pde_users; /* number of callers into module in progress */ spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ struct completion *pde_unload_completion; + struct list_head pde_openers; /* who did ->open, but not ->release */ }; struct kcore_list { -- cgit From 3ae4eed34be0177a8e003411a84e4ee212adbced Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 01:48:34 -0700 Subject: proper pid{hash,map}_init() prototypes This patch adds proper prototypes for pid{hash,map}_init() in include/linux/pid_namespace.h Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pid_namespace.h | 3 +++ init/main.c | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index caff5283d15c..1a49ab5ec7b9 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -85,4 +85,7 @@ static inline struct task_struct *task_child_reaper(struct task_struct *tsk) return tsk->nsproxy->pid_ns->child_reaper; } +void pidhash_init(void); +void pidmap_init(void); + #endif /* _LINUX_PID_NS_H */ diff --git a/init/main.c b/init/main.c index 2769dc031c62..0604cbcaf1e4 100644 --- a/init/main.c +++ b/init/main.c @@ -87,8 +87,6 @@ extern void init_IRQ(void); extern void fork_init(unsigned long); extern void mca_init(void); extern void sbus_init(void); -extern void pidhash_init(void); -extern void pidmap_init(void); extern void prio_tree_init(void); extern void radix_tree_init(void); extern void free_initmem(void); -- cgit From 33166b1ffca5e1945246bcaa77d72a22b0d3e531 Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Fri, 25 Jul 2008 01:48:35 -0700 Subject: shrink struct pid by removing padding on 64 bit builds When struct pid is built on a 64 bit platform gcc has to insert padding to maintain the correct alignment, by simply reordering its members the memory usage shrinks from 88 bytes to 80. I've successfully run with this patch on my desktop AMD64 machine. There are no significant kernel size changes to a default config.X86_64 on the latest git v2.6.26-rc1 text data bss dec hex filename 5404828 976760 734280 7115868 6c945c vmlinux 5404811 976760 734280 7115851 6c944b vmlinux.pid-patch Acked-by: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pid.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/pid.h b/include/linux/pid.h index c21c7e8124a7..6f084b9e2c40 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -57,10 +57,10 @@ struct upid { struct pid { atomic_t count; + unsigned int level; /* lists of tasks that use this pid */ struct hlist_head tasks[PIDTYPE_MAX]; struct rcu_head rcu; - unsigned int level; struct upid numbers[1]; }; -- cgit From 19b0cfcca41dd772065671ad0584e1cea0f3fd13 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:48:35 -0700 Subject: pidns: remove now unused kill_proc function This function operated on a pid_t to kill a task, which is no longer valid in a containerized system. It has finally lost all its users and we can safely remove it from the tree. Signed-off-by: Pavel Emelyanov Cc: Oleg Nesterov Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 1 - kernel/signal.c | 12 ------------ 2 files changed, 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 0560999eb1db..134cb5cb506c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1800,7 +1800,6 @@ extern void force_sig(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *); extern int send_sig(int, struct task_struct *, int); extern void zap_other_threads(struct task_struct *p); -extern int kill_proc(pid_t, int, int); extern struct sigqueue *sigqueue_alloc(void); extern void sigqueue_free(struct sigqueue *); extern int send_sigqueue(struct sigqueue *, struct task_struct *, int group); diff --git a/kernel/signal.c b/kernel/signal.c index 5c7b7eaa0dc6..82c3545596c5 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1228,17 +1228,6 @@ int kill_pid(struct pid *pid, int sig, int priv) } EXPORT_SYMBOL(kill_pid); -int -kill_proc(pid_t pid, int sig, int priv) -{ - int ret; - - rcu_read_lock(); - ret = kill_pid_info(sig, __si_special(priv), find_pid(pid)); - rcu_read_unlock(); - return ret; -} - /* * These functions support sending signals using preallocated sigqueue * structures. This is needed "because realtime applications cannot @@ -1906,7 +1895,6 @@ EXPORT_SYMBOL(recalc_sigpending); EXPORT_SYMBOL_GPL(dequeue_signal); EXPORT_SYMBOL(flush_signals); EXPORT_SYMBOL(force_sig); -EXPORT_SYMBOL(kill_proc); EXPORT_SYMBOL(ptrace_notify); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); -- cgit From e49859e71e0318b564de1546bdc30fab738f9deb Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:48:36 -0700 Subject: pidns: remove now unused find_pid function. This one had the only users so far - the kill_proc, which is removed, so drop this (invalid in namespaced world) call too. And of course - erase all references on it from comments. Signed-off-by: Pavel Emelyanov Cc: Oleg Nesterov Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pid.h | 4 +--- include/linux/sched.h | 2 +- kernel/pid.c | 8 +------- 3 files changed, 3 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pid.h b/include/linux/pid.h index 6f084b9e2c40..ff1b2a5814d4 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -48,7 +48,7 @@ enum pid_type */ struct upid { - /* Try to keep pid_chain in the same cacheline as nr for find_pid */ + /* Try to keep pid_chain in the same cacheline as nr for find_vpid */ int nr; struct pid_namespace *ns; struct hlist_node pid_chain; @@ -105,14 +105,12 @@ extern struct pid_namespace init_pid_ns; * or rcu_read_lock() held. * * find_pid_ns() finds the pid in the namespace specified - * find_pid() find the pid by its global id, i.e. in the init namespace * find_vpid() finr the pid by its virtual id, i.e. in the current namespace * * see also find_task_by_pid() set in include/linux/sched.h */ extern struct pid *find_pid_ns(int nr, struct pid_namespace *ns); extern struct pid *find_vpid(int nr); -extern struct pid *find_pid(int nr); /* * Lookup a PID in the hash table, and return with it's count elevated. diff --git a/include/linux/sched.h b/include/linux/sched.h index 134cb5cb506c..182da1550fad 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1718,7 +1718,7 @@ extern struct pid_namespace init_pid_ns; * find_task_by_pid(): * finds a task by its global pid * - * see also find_pid() etc in include/linux/pid.h + * see also find_vpid() etc in include/linux/pid.h */ extern struct task_struct *find_task_by_pid_type_ns(int type, int pid, diff --git a/kernel/pid.c b/kernel/pid.c index 753fd90d9ec1..064e76afa507 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -309,12 +309,6 @@ struct pid *find_vpid(int nr) } EXPORT_SYMBOL_GPL(find_vpid); -struct pid *find_pid(int nr) -{ - return find_pid_ns(nr, &init_pid_ns); -} -EXPORT_SYMBOL_GPL(find_pid); - /* * attach_pid() must be called with the tasklist_lock write-held. */ @@ -483,7 +477,7 @@ EXPORT_SYMBOL(task_session_nr_ns); /* * Used by proc to find the first pid that is greater then or equal to nr. * - * If there is a pid at nr this function is exactly the same as find_pid. + * If there is a pid at nr this function is exactly the same as find_pid_ns. */ struct pid *find_ge_pid(int nr, struct pid_namespace *ns) { -- cgit From dbda0de52618d13d1b927c7ba7bb839cfddc4e8c Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:48:37 -0700 Subject: pidns: remove find_task_by_pid, unused for a long time It seems to me that it was a mistake marking this function as deprecated and scheduling it for removal, rather than resolutely removing it after the last caller's death. Anyway - better late, then never. Signed-off-by: Pavel Emelyanov Cc: Oleg Nesterov Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/feature-removal-schedule.txt | 18 ------------------ include/linux/pid.h | 2 +- include/linux/sched.h | 6 ------ 3 files changed, 1 insertion(+), 25 deletions(-) (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 09c4a1efb8e3..721c71b86e06 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -138,24 +138,6 @@ Who: Kay Sievers --------------------------- -What: find_task_by_pid -When: 2.6.26 -Why: With pid namespaces, calling this funciton will return the - wrong task when called from inside a namespace. - - The best way to save a task pid and find a task by this - pid later, is to find this task's struct pid pointer (or get - it directly from the task) and call pid_task() later. - - If someone really needs to get a task by its pid_t, then - he most likely needs the find_task_by_vpid() to get the - task from the same namespace as the current task is in, but - this may be not so in general. - -Who: Pavel Emelyanov - ---------------------------- - What: ACPI procfs interface When: July 2008 Why: ACPI sysfs conversion should be finished by January 2008. diff --git a/include/linux/pid.h b/include/linux/pid.h index ff1b2a5814d4..22921ac4cfd9 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -107,7 +107,7 @@ extern struct pid_namespace init_pid_ns; * find_pid_ns() finds the pid in the namespace specified * find_vpid() finr the pid by its virtual id, i.e. in the current namespace * - * see also find_task_by_pid() set in include/linux/sched.h + * see also find_task_by_vpid() set in include/linux/sched.h */ extern struct pid *find_pid_ns(int nr, struct pid_namespace *ns); extern struct pid *find_vpid(int nr); diff --git a/include/linux/sched.h b/include/linux/sched.h index 182da1550fad..354ef478a80d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1715,8 +1715,6 @@ extern struct pid_namespace init_pid_ns; * finds a task by its pid in the specified namespace * find_task_by_vpid(): * finds a task by its virtual pid - * find_task_by_pid(): - * finds a task by its global pid * * see also find_vpid() etc in include/linux/pid.h */ @@ -1724,10 +1722,6 @@ extern struct pid_namespace init_pid_ns; extern struct task_struct *find_task_by_pid_type_ns(int type, int pid, struct pid_namespace *ns); -static inline struct task_struct *__deprecated find_task_by_pid(pid_t nr) -{ - return find_task_by_pid_type_ns(PIDTYPE_PID, nr, &init_pid_ns); -} extern struct task_struct *find_task_by_vpid(pid_t nr); extern struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns); -- cgit From 49b5cf34727a6c1be1568ab28e89a2d9a6bf51e0 Mon Sep 17 00:00:00 2001 From: Jonathan Lim Date: Fri, 25 Jul 2008 01:48:40 -0700 Subject: accounting: account for user time when updating memory integrals Adapt acct_update_integrals() to include user time when calculating the time difference. The units of acct_rss_mem1 and acct_vm_mem1 are also changed from pages-jiffies to pages-usecs to avoid calling jiffies_to_usecs() in xacct_add_tsk() which might overflow. Signed-off-by: Jonathan Lim Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 2 +- kernel/sched.c | 2 ++ kernel/tsacct.c | 21 ++++++++++++++------- 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 354ef478a80d..af780f299c7c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1257,7 +1257,7 @@ struct task_struct { #if defined(CONFIG_TASK_XACCT) u64 acct_rss_mem1; /* accumulated rss usage */ u64 acct_vm_mem1; /* accumulated virtual memory usage */ - cputime_t acct_stimexpd;/* stime since last update */ + cputime_t acct_timexpd; /* stime + utime since last update */ #endif #ifdef CONFIG_CPUSETS nodemask_t mems_allowed; diff --git a/kernel/sched.c b/kernel/sched.c index 6acf749d3336..0047bd9b96aa 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4046,6 +4046,8 @@ void account_user_time(struct task_struct *p, cputime_t cputime) cpustat->nice = cputime64_add(cpustat->nice, tmp); else cpustat->user = cputime64_add(cpustat->user, tmp); + /* Account for user time used */ + acct_update_integrals(p); } /* diff --git a/kernel/tsacct.c b/kernel/tsacct.c index 4ab1b584961b..1da6990af8e0 100644 --- a/kernel/tsacct.c +++ b/kernel/tsacct.c @@ -84,9 +84,9 @@ void xacct_add_tsk(struct taskstats *stats, struct task_struct *p) { struct mm_struct *mm; - /* convert pages-jiffies to Mbyte-usec */ - stats->coremem = jiffies_to_usecs(p->acct_rss_mem1) * PAGE_SIZE / MB; - stats->virtmem = jiffies_to_usecs(p->acct_vm_mem1) * PAGE_SIZE / MB; + /* convert pages-usec to Mbyte-usec */ + stats->coremem = p->acct_rss_mem1 * PAGE_SIZE / MB; + stats->virtmem = p->acct_vm_mem1 * PAGE_SIZE / MB; mm = get_task_mm(p); if (mm) { /* adjust to KB unit */ @@ -118,12 +118,19 @@ void xacct_add_tsk(struct taskstats *stats, struct task_struct *p) void acct_update_integrals(struct task_struct *tsk) { if (likely(tsk->mm)) { - long delta = cputime_to_jiffies( - cputime_sub(tsk->stime, tsk->acct_stimexpd)); + cputime_t time, dtime; + struct timeval value; + u64 delta; + + time = tsk->stime + tsk->utime; + dtime = cputime_sub(time, tsk->acct_timexpd); + jiffies_to_timeval(cputime_to_jiffies(dtime), &value); + delta = value.tv_sec; + delta = delta * USEC_PER_SEC + value.tv_usec; if (delta == 0) return; - tsk->acct_stimexpd = tsk->stime; + tsk->acct_timexpd = time; tsk->acct_rss_mem1 += delta * get_mm_rss(tsk->mm); tsk->acct_vm_mem1 += delta * tsk->mm->total_vm; } @@ -135,7 +142,7 @@ void acct_update_integrals(struct task_struct *tsk) */ void acct_clear_integrals(struct task_struct *tsk) { - tsk->acct_stimexpd = 0; + tsk->acct_timexpd = 0; tsk->acct_rss_mem1 = 0; tsk->acct_vm_mem1 = 0; } -- cgit From 20fad13ac66ac001c19220d3d08b4de5b6cca6e1 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:48:43 -0700 Subject: pidns: add the struct bsd_acct_struct pointer on pid_namespace struct All the bsdacct-related info will be stored in the area, pointer by this one. It will be NULL automatically for all new namespaces. Signed-off-by: Pavel Emelyanov Cc: Balbir Singh Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pid_namespace.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index 1a49ab5ec7b9..1af82c4e17d4 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -14,6 +14,8 @@ struct pidmap { #define PIDMAP_ENTRIES ((PID_MAX_LIMIT + 8*PAGE_SIZE - 1)/PAGE_SIZE/8) +struct bsd_acct_struct; + struct pid_namespace { struct kref kref; struct pidmap pidmap[PIDMAP_ENTRIES]; @@ -25,6 +27,9 @@ struct pid_namespace { #ifdef CONFIG_PROC_FS struct vfsmount *proc_mnt; #endif +#ifdef CONFIG_BSD_PROCESS_ACCT + struct bsd_acct_struct *bacct; +#endif }; extern struct pid_namespace init_pid_ns; -- cgit From 0b6b030fc30d169bb406b34b4fc60d99dde4a9c6 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 25 Jul 2008 01:48:47 -0700 Subject: bsdacct: switch from global bsd_acct_struct instance to per-pidns one Allocate the structure on the first call to sys_acct(). After this each namespace, that ordered the accounting, will live with this structure till its own death. Two notes - routines, that close the accounting on fs umount time use the init_pid_ns's acct by now; - accounting routine accounts to dying task's namespace (also by now). Signed-off-by: Pavel Emelyanov Cc: Balbir Singh Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/acct.h | 3 ++ kernel/acct.c | 84 +++++++++++++++++++++++++++++++++++++++----------- kernel/pid_namespace.c | 2 ++ 3 files changed, 71 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/acct.h b/include/linux/acct.h index e8cae54e8d88..882dc7248766 100644 --- a/include/linux/acct.h +++ b/include/linux/acct.h @@ -120,17 +120,20 @@ struct acct_v3 struct vfsmount; struct super_block; struct pacct_struct; +struct pid_namespace; extern void acct_auto_close_mnt(struct vfsmount *m); extern void acct_auto_close(struct super_block *sb); extern void acct_init_pacct(struct pacct_struct *pacct); extern void acct_collect(long exitcode, int group_dead); extern void acct_process(void); +extern void acct_exit_ns(struct pid_namespace *); #else #define acct_auto_close_mnt(x) do { } while (0) #define acct_auto_close(x) do { } while (0) #define acct_init_pacct(x) do { } while (0) #define acct_collect(x,y) do { } while (0) #define acct_process() do { } while (0) +#define acct_exit_ns(ns) do { } while (0) #endif /* diff --git a/kernel/acct.c b/kernel/acct.c index 72d4760c8da8..febbbc67157e 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -93,8 +93,6 @@ struct bsd_acct_struct { static DEFINE_SPINLOCK(acct_lock); -static struct bsd_acct_struct acct_globals __cacheline_aligned; - /* * Called whenever the timer says to check the free space. */ @@ -176,7 +174,8 @@ out: * * NOTE: acct_lock MUST be held on entry and exit. */ -static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file) +static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file, + struct pid_namespace *ns) { struct file *old_acct = NULL; struct pid_namespace *old_ns = NULL; @@ -188,10 +187,11 @@ static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file) acct->active = 0; acct->needcheck = 0; acct->file = NULL; + acct->ns = NULL; } if (file) { acct->file = file; - acct->ns = get_pid_ns(task_active_pid_ns(current)); + acct->ns = ns; acct->needcheck = 0; acct->active = 1; /* It's been deleted if it was used before so this is safe */ @@ -204,7 +204,6 @@ static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file) spin_unlock(&acct_lock); do_acct_process(acct, old_ns, old_acct); filp_close(old_acct, NULL); - put_pid_ns(old_ns); spin_lock(&acct_lock); } } @@ -213,6 +212,8 @@ static int acct_on(char *name) { struct file *file; int error; + struct pid_namespace *ns; + struct bsd_acct_struct *acct = NULL; /* Difference from BSD - they don't do O_APPEND */ file = filp_open(name, O_WRONLY|O_APPEND|O_LARGEFILE, 0); @@ -229,18 +230,34 @@ static int acct_on(char *name) return -EIO; } + ns = task_active_pid_ns(current); + if (ns->bacct == NULL) { + acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL); + if (acct == NULL) { + filp_close(file, NULL); + return -ENOMEM; + } + } + error = security_acct(file); if (error) { + kfree(acct); filp_close(file, NULL); return error; } spin_lock(&acct_lock); + if (ns->bacct == NULL) { + ns->bacct = acct; + acct = NULL; + } + mnt_pin(file->f_path.mnt); - acct_file_reopen(&acct_globals, file); + acct_file_reopen(ns->bacct, file, ns); spin_unlock(&acct_lock); mntput(file->f_path.mnt); /* it's pinned, now give up active reference */ + kfree(acct); return 0; } @@ -270,10 +287,16 @@ asmlinkage long sys_acct(const char __user *name) error = acct_on(tmp); putname(tmp); } else { + struct bsd_acct_struct *acct; + + acct = task_active_pid_ns(current)->bacct; + if (acct == NULL) + return 0; + error = security_acct(NULL); if (!error) { spin_lock(&acct_lock); - acct_file_reopen(&acct_globals, NULL); + acct_file_reopen(acct, NULL, NULL); spin_unlock(&acct_lock); } } @@ -289,9 +312,15 @@ asmlinkage long sys_acct(const char __user *name) */ void acct_auto_close_mnt(struct vfsmount *m) { + struct bsd_acct_struct *acct; + + acct = init_pid_ns.bacct; + if (acct == NULL) + return; + spin_lock(&acct_lock); - if (acct_globals.file && acct_globals.file->f_path.mnt == m) - acct_file_reopen(&acct_globals, NULL); + if (acct->file && acct->file->f_path.mnt == m) + acct_file_reopen(acct, NULL, NULL); spin_unlock(&acct_lock); } @@ -304,10 +333,29 @@ void acct_auto_close_mnt(struct vfsmount *m) */ void acct_auto_close(struct super_block *sb) { + struct bsd_acct_struct *acct; + + acct = init_pid_ns.bacct; + if (acct == NULL) + return; + spin_lock(&acct_lock); - if (acct_globals.file && - acct_globals.file->f_path.mnt->mnt_sb == sb) { - acct_file_reopen(&acct_globals, NULL); + if (acct->file && acct->file->f_path.mnt->mnt_sb == sb) + acct_file_reopen(acct, NULL, NULL); + spin_unlock(&acct_lock); +} + +void acct_exit_ns(struct pid_namespace *ns) +{ + struct bsd_acct_struct *acct; + + spin_lock(&acct_lock); + acct = ns->bacct; + if (acct != NULL) { + if (acct->file != NULL) + acct_file_reopen(acct, NULL, NULL); + + kfree(acct); } spin_unlock(&acct_lock); } @@ -587,25 +635,25 @@ void acct_collect(long exitcode, int group_dead) void acct_process(void) { struct file *file = NULL; - struct pid_namespace *ns; + struct pid_namespace *ns = task_active_pid_ns(current); + struct bsd_acct_struct *acct; + acct = ns->bacct; /* * accelerate the common fastpath: */ - if (!acct_globals.file) + if (!acct || !acct->file) return; spin_lock(&acct_lock); - file = acct_globals.file; + file = acct->file; if (unlikely(!file)) { spin_unlock(&acct_lock); return; } get_file(file); - ns = get_pid_ns(acct_globals.ns); spin_unlock(&acct_lock); - do_acct_process(&acct_globals, ns, file); + do_acct_process(acct, ns, file); fput(file); - put_pid_ns(ns); } diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c index 06331cc1c3f5..ea567b78d1aa 100644 --- a/kernel/pid_namespace.c +++ b/kernel/pid_namespace.c @@ -12,6 +12,7 @@ #include #include #include +#include #define BITS_PER_PAGE (PAGE_SIZE*8) @@ -181,6 +182,7 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns) /* Child reaper for the pid namespace is going away */ pid_ns->child_reaper = NULL; + acct_exit_ns(pid_ns); return; } -- cgit From 297c5d92634c809cef23d73e7b2556f2528ff7e2 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Fri, 25 Jul 2008 01:48:49 -0700 Subject: task IO accounting: provide distinct tgid/tid I/O statistics Report per-thread I/O statistics in /proc/pid/task/tid/io and aggregate parent I/O statistics in /proc/pid/io. This approach follows the same model used to account per-process and per-thread CPU times. As a practial application, this allows for example to quickly find the top I/O consumer when a process spawns many child threads that perform the actual I/O work, because the aggregated I/O statistics can always be found in /proc/pid/io. [ Oleg Nesterov points out that we should check that the task is still alive before we iterate over the threads, but also says that we can do that fixup on top of this later. - Linus ] Acked-by: Balbir Singh Signed-off-by: Andrea Righi Cc: Matt Heaton Cc: Shailabh Nagar Acked-by-with-comments: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/base.c | 86 ++++++++++++++++++++++++++++++++++++++++++--------- include/linux/sched.h | 4 +++ kernel/exit.c | 27 ++++++++++++++++ kernel/fork.c | 6 ++++ 4 files changed, 108 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/base.c b/fs/proc/base.c index 58c3e6a8e15e..a891fe4cb43b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2376,29 +2376,82 @@ static int proc_base_fill_cache(struct file *filp, void *dirent, } #ifdef CONFIG_TASK_IO_ACCOUNTING -static int proc_pid_io_accounting(struct task_struct *task, char *buffer) -{ +static int do_io_accounting(struct task_struct *task, char *buffer, int whole) +{ + u64 rchar, wchar, syscr, syscw; + struct task_io_accounting ioac; + + if (!whole) { + rchar = task->rchar; + wchar = task->wchar; + syscr = task->syscr; + syscw = task->syscw; + memcpy(&ioac, &task->ioac, sizeof(ioac)); + } else { + unsigned long flags; + struct task_struct *t = task; + rchar = wchar = syscr = syscw = 0; + memset(&ioac, 0, sizeof(ioac)); + + rcu_read_lock(); + do { + rchar += t->rchar; + wchar += t->wchar; + syscr += t->syscr; + syscw += t->syscw; + + ioac.read_bytes += t->ioac.read_bytes; + ioac.write_bytes += t->ioac.write_bytes; + ioac.cancelled_write_bytes += + t->ioac.cancelled_write_bytes; + t = next_thread(t); + } while (t != task); + rcu_read_unlock(); + + if (lock_task_sighand(task, &flags)) { + struct signal_struct *sig = task->signal; + + rchar += sig->rchar; + wchar += sig->wchar; + syscr += sig->syscr; + syscw += sig->syscw; + + ioac.read_bytes += sig->ioac.read_bytes; + ioac.write_bytes += sig->ioac.write_bytes; + ioac.cancelled_write_bytes += + sig->ioac.cancelled_write_bytes; + + unlock_task_sighand(task, &flags); + } + } + return sprintf(buffer, -#ifdef CONFIG_TASK_XACCT "rchar: %llu\n" "wchar: %llu\n" "syscr: %llu\n" "syscw: %llu\n" -#endif "read_bytes: %llu\n" "write_bytes: %llu\n" "cancelled_write_bytes: %llu\n", -#ifdef CONFIG_TASK_XACCT - (unsigned long long)task->rchar, - (unsigned long long)task->wchar, - (unsigned long long)task->syscr, - (unsigned long long)task->syscw, -#endif - (unsigned long long)task->ioac.read_bytes, - (unsigned long long)task->ioac.write_bytes, - (unsigned long long)task->ioac.cancelled_write_bytes); + (unsigned long long)rchar, + (unsigned long long)wchar, + (unsigned long long)syscr, + (unsigned long long)syscw, + (unsigned long long)ioac.read_bytes, + (unsigned long long)ioac.write_bytes, + (unsigned long long)ioac.cancelled_write_bytes); +} + +static int proc_tid_io_accounting(struct task_struct *task, char *buffer) +{ + return do_io_accounting(task, buffer, 0); } -#endif + +static int proc_tgid_io_accounting(struct task_struct *task, char *buffer) +{ + return do_io_accounting(task, buffer, 1); +} +#endif /* CONFIG_TASK_IO_ACCOUNTING */ /* * Thread groups @@ -2470,7 +2523,7 @@ static const struct pid_entry tgid_base_stuff[] = { REG("coredump_filter", S_IRUGO|S_IWUSR, coredump_filter), #endif #ifdef CONFIG_TASK_IO_ACCOUNTING - INF("io", S_IRUGO, pid_io_accounting), + INF("io", S_IRUGO, tgid_io_accounting), #endif }; @@ -2797,6 +2850,9 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_FAULT_INJECTION REG("make-it-fail", S_IRUGO|S_IWUSR, fault_inject), #endif +#ifdef CONFIG_TASK_IO_ACCOUNTING + INF("io", S_IRUGO, tid_io_accounting), +#endif }; static int proc_tid_base_readdir(struct file * filp, diff --git a/include/linux/sched.h b/include/linux/sched.h index af780f299c7c..d22ffe06d0eb 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -506,6 +506,10 @@ struct signal_struct { unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw; unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt; unsigned long inblock, oublock, cinblock, coublock; +#ifdef CONFIG_TASK_XACCT + u64 rchar, wchar, syscr, syscw; +#endif + struct task_io_accounting ioac; /* * Cumulative ns of scheduled CPU time for dead threads in the diff --git a/kernel/exit.c b/kernel/exit.c index 8a4d4d12e294..ad933bb29ec7 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -120,6 +120,18 @@ static void __exit_signal(struct task_struct *tsk) sig->nivcsw += tsk->nivcsw; sig->inblock += task_io_get_inblock(tsk); sig->oublock += task_io_get_oublock(tsk); +#ifdef CONFIG_TASK_XACCT + sig->rchar += tsk->rchar; + sig->wchar += tsk->wchar; + sig->syscr += tsk->syscr; + sig->syscw += tsk->syscw; +#endif /* CONFIG_TASK_XACCT */ +#ifdef CONFIG_TASK_IO_ACCOUNTING + sig->ioac.read_bytes += tsk->ioac.read_bytes; + sig->ioac.write_bytes += tsk->ioac.write_bytes; + sig->ioac.cancelled_write_bytes += + tsk->ioac.cancelled_write_bytes; +#endif /* CONFIG_TASK_IO_ACCOUNTING */ sig->sum_sched_runtime += tsk->se.sum_exec_runtime; sig = NULL; /* Marker for below. */ } @@ -1366,6 +1378,21 @@ static int wait_task_zombie(struct task_struct *p, int options, psig->coublock += task_io_get_oublock(p) + sig->oublock + sig->coublock; +#ifdef CONFIG_TASK_XACCT + psig->rchar += p->rchar + sig->rchar; + psig->wchar += p->wchar + sig->wchar; + psig->syscr += p->syscr + sig->syscr; + psig->syscw += p->syscw + sig->syscw; +#endif /* CONFIG_TASK_XACCT */ +#ifdef CONFIG_TASK_IO_ACCOUNTING + psig->ioac.read_bytes += + p->ioac.read_bytes + sig->ioac.read_bytes; + psig->ioac.write_bytes += + p->ioac.write_bytes + sig->ioac.write_bytes; + psig->ioac.cancelled_write_bytes += + p->ioac.cancelled_write_bytes + + sig->ioac.cancelled_write_bytes; +#endif /* CONFIG_TASK_IO_ACCOUNTING */ spin_unlock_irq(&p->parent->sighand->siglock); } diff --git a/kernel/fork.c b/kernel/fork.c index 813d5c89b9d5..b99d73e971a4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -812,6 +812,12 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0; sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0; sig->inblock = sig->oublock = sig->cinblock = sig->coublock = 0; +#ifdef CONFIG_TASK_XACCT + sig->rchar = sig->wchar = sig->syscr = sig->syscw = 0; +#endif +#ifdef CONFIG_TASK_IO_ACCOUNTING + memset(&sig->ioac, 0, sizeof(sig->ioac)); +#endif sig->sum_sched_runtime = 0; INIT_LIST_HEAD(&sig->cpu_timers[0]); INIT_LIST_HEAD(&sig->cpu_timers[1]); -- cgit From 873b47717732c2f33a4b14de02571a4295a02f0c Mon Sep 17 00:00:00 2001 From: Keika Kobayashi Date: Fri, 25 Jul 2008 01:48:52 -0700 Subject: per-task-delay-accounting: add memory reclaim delay Sometimes, application responses become bad under heavy memory load. Applications take a bit time to reclaim memory. The statistics, how long memory reclaim takes, will be useful to measure memory usage. This patch adds accounting memory reclaim to per-task-delay-accounting for accounting the time of do_try_to_free_pages(). - When System is under low memory load, memory reclaim may not occur. $ free total used free shared buffers cached Mem: 8197800 1577300 6620500 0 4808 1516724 -/+ buffers/cache: 55768 8142032 Swap: 16386292 0 16386292 $ vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 0 5069748 10612 3014060 0 0 0 0 3 26 0 0 100 0 0 0 0 5069748 10612 3014060 0 0 0 0 4 22 0 0 100 0 0 0 0 5069748 10612 3014060 0 0 0 0 3 18 0 0 100 0 Measure the time of tar command. $ ls -s test.dat 1501472 test.dat $ time tar cvf test.tar test.dat real 0m13.388s user 0m0.116s sys 0m5.304s $ ./delayget -d -p CPU count real total virtual total delay total 428 5528345500 5477116080 62749891 IO count delay total 338 8078977189 SWAP count delay total 0 0 RECLAIM count delay total 0 0 - When system is under heavy memory load memory reclaim may occur. $ vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 7159032 49724 1812 3012 0 0 0 0 3 24 0 0 100 0 0 0 7159032 49724 1812 3012 0 0 0 0 4 24 0 0 100 0 0 0 7159032 49848 1812 3012 0 0 0 0 3 22 0 0 100 0 In this case, one process uses more 8G memory by execution of malloc() and memset(). $ time tar cvf test.tar test.dat real 1m38.563s <- increased by 85 sec user 0m0.140s sys 0m7.060s $ ./delayget -d -p CPU count real total virtual total delay total 9021 7140446250 7315277975 923201824 IO count delay total 8965 90466349669 SWAP count delay total 3 21036367 RECLAIM count delay total 740 61011951153 In the later case, the value of RECLAIM is increasing. So, taskstats can show how much memory reclaim influences TAT. Signed-off-by: Keika Kobayashi Acked-by: Balbir Singh Acked-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/delayacct.h | 19 +++++++++++++++++++ include/linux/sched.h | 4 ++++ kernel/delayacct.c | 13 +++++++++++++ mm/vmscan.c | 5 +++++ 4 files changed, 41 insertions(+) (limited to 'include/linux') diff --git a/include/linux/delayacct.h b/include/linux/delayacct.h index ab94bc083558..f352f06fa063 100644 --- a/include/linux/delayacct.h +++ b/include/linux/delayacct.h @@ -39,6 +39,8 @@ extern void __delayacct_blkio_start(void); extern void __delayacct_blkio_end(void); extern int __delayacct_add_tsk(struct taskstats *, struct task_struct *); extern __u64 __delayacct_blkio_ticks(struct task_struct *); +extern void __delayacct_freepages_start(void); +extern void __delayacct_freepages_end(void); static inline int delayacct_is_task_waiting_on_io(struct task_struct *p) { @@ -107,6 +109,18 @@ static inline __u64 delayacct_blkio_ticks(struct task_struct *tsk) return 0; } +static inline void delayacct_freepages_start(void) +{ + if (current->delays) + __delayacct_freepages_start(); +} + +static inline void delayacct_freepages_end(void) +{ + if (current->delays) + __delayacct_freepages_end(); +} + #else static inline void delayacct_set_flag(int flag) {} @@ -129,6 +143,11 @@ static inline __u64 delayacct_blkio_ticks(struct task_struct *tsk) { return 0; } static inline int delayacct_is_task_waiting_on_io(struct task_struct *p) { return 0; } +static inline void delayacct_freepages_start(void) +{} +static inline void delayacct_freepages_end(void) +{} + #endif /* CONFIG_TASK_DELAY_ACCT */ #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index d22ffe06d0eb..42036ffe6b00 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -672,6 +672,10 @@ struct task_delay_info { /* io operations performed */ u32 swapin_count; /* total count of the number of swapin block */ /* io operations performed */ + + struct timespec freepages_start, freepages_end; + u64 freepages_delay; /* wait for memory reclaim */ + u32 freepages_count; /* total count of memory reclaim */ }; #endif /* CONFIG_TASK_DELAY_ACCT */ diff --git a/kernel/delayacct.c b/kernel/delayacct.c index 10e43fd8b721..84b6782a2ce4 100644 --- a/kernel/delayacct.c +++ b/kernel/delayacct.c @@ -165,3 +165,16 @@ __u64 __delayacct_blkio_ticks(struct task_struct *tsk) return ret; } +void __delayacct_freepages_start(void) +{ + delayacct_start(¤t->delays->freepages_start); +} + +void __delayacct_freepages_end(void) +{ + delayacct_end(¤t->delays->freepages_start, + ¤t->delays->freepages_end, + ¤t->delays->freepages_delay, + ¤t->delays->freepages_count); +} + diff --git a/mm/vmscan.c b/mm/vmscan.c index 967d30ccd92b..26672c6cd3ce 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -1316,6 +1317,8 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, struct zone *zone; enum zone_type high_zoneidx = gfp_zone(sc->gfp_mask); + delayacct_freepages_start(); + if (scan_global_lru(sc)) count_vm_event(ALLOCSTALL); /* @@ -1396,6 +1399,8 @@ out: } else mem_cgroup_record_reclaim_priority(sc->mem_cgroup, priority); + delayacct_freepages_end(); + return ret; } -- cgit From 016ae219b920c4e606088761d3d6070cdf8ba706 Mon Sep 17 00:00:00 2001 From: Keika Kobayashi Date: Fri, 25 Jul 2008 01:48:53 -0700 Subject: per-task-delay-accounting: update taskstats for memory reclaim delay Add members for memory reclaim delay to taskstats, and accumulate them in __delayacct_add_tsk() . Signed-off-by: Keika Kobayashi Cc: Hiroshi Shimamoto Cc: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/accounting/taskstats-struct.txt | 7 +++++++ include/linux/taskstats.h | 6 +++++- kernel/delayacct.c | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/accounting/taskstats-struct.txt b/Documentation/accounting/taskstats-struct.txt index cd784f46bf8a..b988d110db59 100644 --- a/Documentation/accounting/taskstats-struct.txt +++ b/Documentation/accounting/taskstats-struct.txt @@ -26,6 +26,8 @@ There are three different groups of fields in the struct taskstats: 5) Time accounting for SMT machines +6) Extended delay accounting fields for memory reclaim + Future extension should add fields to the end of the taskstats struct, and should not change the relative position of each field within the struct. @@ -170,4 +172,9 @@ struct taskstats { __u64 ac_utimescaled; /* utime scaled on frequency etc */ __u64 ac_stimescaled; /* stime scaled on frequency etc */ __u64 cpu_scaled_run_real_total; /* scaled cpu_run_real_total */ + +6) Extended delay accounting fields for memory reclaim + /* Delay waiting for memory reclaim */ + __u64 freepages_count; + __u64 freepages_delay_total; } diff --git a/include/linux/taskstats.h b/include/linux/taskstats.h index 5d69c0744fff..18269e956a71 100644 --- a/include/linux/taskstats.h +++ b/include/linux/taskstats.h @@ -31,7 +31,7 @@ */ -#define TASKSTATS_VERSION 6 +#define TASKSTATS_VERSION 7 #define TS_COMM_LEN 32 /* should be >= TASK_COMM_LEN * in linux/sched.h */ @@ -157,6 +157,10 @@ struct taskstats { __u64 ac_utimescaled; /* utime scaled on frequency etc */ __u64 ac_stimescaled; /* stime scaled on frequency etc */ __u64 cpu_scaled_run_real_total; /* scaled cpu_run_real_total */ + + /* Delay waiting for memory reclaim */ + __u64 freepages_count; + __u64 freepages_delay_total; }; diff --git a/kernel/delayacct.c b/kernel/delayacct.c index 84b6782a2ce4..b3179dad71be 100644 --- a/kernel/delayacct.c +++ b/kernel/delayacct.c @@ -145,8 +145,11 @@ int __delayacct_add_tsk(struct taskstats *d, struct task_struct *tsk) d->blkio_delay_total = (tmp < d->blkio_delay_total) ? 0 : tmp; tmp = d->swapin_delay_total + tsk->delays->swapin_delay; d->swapin_delay_total = (tmp < d->swapin_delay_total) ? 0 : tmp; + tmp = d->freepages_delay_total + tsk->delays->freepages_delay; + d->freepages_delay_total = (tmp < d->freepages_delay_total) ? 0 : tmp; d->blkio_count += tsk->delays->blkio_count; d->swapin_count += tsk->delays->swapin_count; + d->freepages_count += tsk->delays->freepages_count; spin_unlock_irqrestore(&tsk->delays->lock, flags); done: -- cgit From bde74e4bc64415b142e556a34d295a52a1b7da9d Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 25 Jul 2008 01:48:57 -0700 Subject: locks: add special return value for asynchronous locks Use a special error value FILE_LOCK_DEFERRED to mean that a locking operation returned asynchronously. This is returned by posix_lock_file() for sleeping locks to mean that the lock has been queued on the block list, and will be woken up when it might become available and needs to be retried (either fl_lmops->fl_notify() is called or fl_wait is woken up). f_op->lock() to mean either the above, or that the filesystem will call back with fl_lmops->fl_grant() when the result of the locking operation is known. The filesystem can do this for sleeping as well as non-sleeping locks. This is to make sure, that return values of -EAGAIN and -EINPROGRESS by filesystems are not mistaken to mean an asynchronous locking. This also makes error handling in fs/locks.c and lockd/svclock.c slightly cleaner. Signed-off-by: Miklos Szeredi Cc: Trond Myklebust Cc: "J. Bruce Fields" Cc: Matthew Wilcox Cc: David Teigland Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/dlm/plock.c | 2 +- fs/lockd/svclock.c | 13 ++++--------- fs/locks.c | 28 ++++++++++++++-------------- include/linux/fs.h | 6 ++++++ 4 files changed, 25 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/fs/dlm/plock.c b/fs/dlm/plock.c index 78878c5781ca..eba87ff3177b 100644 --- a/fs/dlm/plock.c +++ b/fs/dlm/plock.c @@ -116,7 +116,7 @@ int dlm_posix_lock(dlm_lockspace_t *lockspace, u64 number, struct file *file, if (xop->callback == NULL) wait_event(recv_wq, (op->done != 0)); else { - rv = -EINPROGRESS; + rv = FILE_LOCK_DEFERRED; goto out; } diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 821b9acdfb66..cf0d5c2c318d 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -418,8 +418,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; case -EAGAIN: ret = nlm_lck_denied; - break; - case -EINPROGRESS: + goto out; + case FILE_LOCK_DEFERRED: if (wait) break; /* Filesystem lock operation is in progress @@ -434,10 +434,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; } - ret = nlm_lck_denied; - if (!wait) - goto out; - ret = nlm_lck_blocked; /* Append to list of blocked */ @@ -507,7 +503,7 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, } error = vfs_test_lock(file->f_file, &lock->fl); - if (error == -EINPROGRESS) { + if (error == FILE_LOCK_DEFERRED) { ret = nlmsvc_defer_lock_rqst(rqstp, block); goto out; } @@ -731,8 +727,7 @@ nlmsvc_grant_blocked(struct nlm_block *block) switch (error) { case 0: break; - case -EAGAIN: - case -EINPROGRESS: + case FILE_LOCK_DEFERRED: dprintk("lockd: lock still blocked error %d\n", error); nlmsvc_insert_block(block, NLM_NEVER); nlmsvc_release_block(block); diff --git a/fs/locks.c b/fs/locks.c index dce8c747371c..1ce57b4b362c 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -779,8 +779,10 @@ find_conflict: if (!flock_locks_conflict(request, fl)) continue; error = -EAGAIN; - if (request->fl_flags & FL_SLEEP) - locks_insert_block(fl, request); + if (!(request->fl_flags & FL_SLEEP)) + goto out; + error = FILE_LOCK_DEFERRED; + locks_insert_block(fl, request); goto out; } if (request->fl_flags & FL_ACCESS) @@ -836,7 +838,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str error = -EDEADLK; if (posix_locks_deadlock(request, fl)) goto out; - error = -EAGAIN; + error = FILE_LOCK_DEFERRED; locks_insert_block(fl, request); goto out; } @@ -1035,7 +1037,7 @@ int posix_lock_file_wait(struct file *filp, struct file_lock *fl) might_sleep (); for (;;) { error = posix_lock_file(filp, fl, NULL); - if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) + if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); if (!error) @@ -1107,9 +1109,7 @@ int locks_mandatory_area(int read_write, struct inode *inode, for (;;) { error = __posix_lock_file(inode, &fl, NULL); - if (error != -EAGAIN) - break; - if (!(fl.fl_flags & FL_SLEEP)) + if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl.fl_wait, !fl.fl_next); if (!error) { @@ -1531,7 +1531,7 @@ int flock_lock_file_wait(struct file *filp, struct file_lock *fl) might_sleep(); for (;;) { error = flock_lock_file(filp, fl); - if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) + if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); if (!error) @@ -1716,17 +1716,17 @@ out: * fl_grant is set. Callers expecting ->lock() to return asynchronously * will only use F_SETLK, not F_SETLKW; they will set FL_SLEEP if (and only if) * the request is for a blocking lock. When ->lock() does return asynchronously, - * it must return -EINPROGRESS, and call ->fl_grant() when the lock + * it must return FILE_LOCK_DEFERRED, and call ->fl_grant() when the lock * request completes. * If the request is for non-blocking lock the file system should return - * -EINPROGRESS then try to get the lock and call the callback routine with - * the result. If the request timed out the callback routine will return a + * FILE_LOCK_DEFERRED then try to get the lock and call the callback routine + * with the result. If the request timed out the callback routine will return a * nonzero return code and the file system should release the lock. The file * system is also responsible to keep a corresponding posix lock when it * grants a lock so the VFS can find out which locks are locally held and do * the correct lock cleanup when required. * The underlying filesystem must not drop the kernel lock or call - * ->fl_grant() before returning to the caller with a -EINPROGRESS + * ->fl_grant() before returning to the caller with a FILE_LOCK_DEFERRED * return code. */ int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl, struct file_lock *conf) @@ -1804,7 +1804,7 @@ again: else { for (;;) { error = posix_lock_file(filp, file_lock, NULL); - if (error != -EAGAIN || cmd == F_SETLK) + if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(file_lock->fl_wait, !file_lock->fl_next); @@ -1941,7 +1941,7 @@ again: else { for (;;) { error = posix_lock_file(filp, file_lock, NULL); - if (error != -EAGAIN || cmd == F_SETLK64) + if (error != FILE_LOCK_DEFERRED) break; error = wait_event_interruptible(file_lock->fl_wait, !file_lock->fl_next); diff --git a/include/linux/fs.h b/include/linux/fs.h index 4b86f806014c..49d8eb7a71be 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -885,6 +885,12 @@ static inline int file_check_writeable(struct file *filp) #define FL_CLOSE 64 /* unlock on close */ #define FL_SLEEP 128 /* A blocking lock */ +/* + * Special return value from posix_lock_file() and vfs_lock_file() for + * asynchronous locking. + */ +#define FILE_LOCK_DEFERRED 1 + /* * The POSIX file lock owner is determined by * the "struct files_struct" in the thread group -- cgit From 33670fa296860283f04a7975b8c790f101e43a6e Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 25 Jul 2008 01:49:02 -0700 Subject: fuse: nfs export special lookups Implement the get_parent export operation by sending a LOOKUP request with ".." as the name. Implement looking up an inode by node ID after it has been evicted from the cache. This is done by seding a LOOKUP request with "." as the name (for all file types, not just directories). The filesystem can set the FUSE_EXPORT_SUPPORT flag in the INIT reply, to indicate that it supports these special lookups. Thanks to John Muir for the original implementation of this feature. Signed-off-by: Miklos Szeredi Cc: "J. Bruce Fields" Cc: Trond Myklebust Cc: Matthew Wilcox Cc: David Teigland Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fuse/fuse_i.h | 6 +++++ fs/fuse/inode.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++--- include/linux/fuse.h | 3 +++ 3 files changed, 72 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 5d3146da64e6..3a876076bdd1 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -363,6 +363,9 @@ struct fuse_conn { /** Do not send separate SETATTR request before open(O_TRUNC) */ unsigned atomic_o_trunc : 1; + /** Filesystem supports NFS exporting. Only set in INIT */ + unsigned export_support : 1; + /* * The following bitfields are only for optimization purposes * and hence races in setting them will not cause malfunction @@ -473,6 +476,9 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, int generation, struct fuse_attr *attr, u64 attr_valid, u64 attr_version); +int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, + struct fuse_entry_out *outarg, struct inode **inode); + /** * Send FORGET command */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 71fa76a48a31..7d2f7d6e22e2 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -562,6 +562,7 @@ struct fuse_inode_handle static struct dentry *fuse_get_dentry(struct super_block *sb, struct fuse_inode_handle *handle) { + struct fuse_conn *fc = get_fuse_conn_super(sb); struct inode *inode; struct dentry *entry; int err = -ESTALE; @@ -570,8 +571,27 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, goto out_err; inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); - if (!inode) - goto out_err; + if (!inode) { + struct fuse_entry_out outarg; + struct qstr name; + + if (!fc->export_support) + goto out_err; + + name.len = 1; + name.name = "."; + err = fuse_lookup_name(sb, handle->nodeid, &name, &outarg, + &inode); + if (err && err != -ENOENT) + goto out_err; + if (err || !inode) { + err = -ESTALE; + goto out_err; + } + err = -EIO; + if (get_node_id(inode) != handle->nodeid) + goto out_iput; + } err = -ESTALE; if (inode->i_generation != handle->generation) goto out_iput; @@ -659,11 +679,46 @@ static struct dentry *fuse_fh_to_parent(struct super_block *sb, return fuse_get_dentry(sb, &parent); } +static struct dentry *fuse_get_parent(struct dentry *child) +{ + struct inode *child_inode = child->d_inode; + struct fuse_conn *fc = get_fuse_conn(child_inode); + struct inode *inode; + struct dentry *parent; + struct fuse_entry_out outarg; + struct qstr name; + int err; + + if (!fc->export_support) + return ERR_PTR(-ESTALE); + + name.len = 2; + name.name = ".."; + err = fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode), + &name, &outarg, &inode); + if (err && err != -ENOENT) + return ERR_PTR(err); + if (err || !inode) + return ERR_PTR(-ESTALE); + + parent = d_alloc_anon(inode); + if (!parent) { + iput(inode); + return ERR_PTR(-ENOMEM); + } + if (get_node_id(inode) != FUSE_ROOT_ID) { + parent->d_op = &fuse_dentry_operations; + fuse_invalidate_entry_cache(parent); + } + + return parent; +} static const struct export_operations fuse_export_operations = { .fh_to_dentry = fuse_fh_to_dentry, .fh_to_parent = fuse_fh_to_parent, .encode_fh = fuse_encode_fh, + .get_parent = fuse_get_parent, }; static const struct super_operations fuse_super_operations = { @@ -695,6 +750,11 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) fc->no_lock = 1; if (arg->flags & FUSE_ATOMIC_O_TRUNC) fc->atomic_o_trunc = 1; + if (arg->minor >= 9) { + /* LOOKUP has dependency on proto version */ + if (arg->flags & FUSE_EXPORT_SUPPORT) + fc->export_support = 1; + } if (arg->flags & FUSE_BIG_WRITES) fc->big_writes = 1; } else { @@ -721,7 +781,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) arg->minor = FUSE_KERNEL_MINOR_VERSION; arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE; arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC | - FUSE_BIG_WRITES; + FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES; req->in.h.opcode = FUSE_INIT; req->in.numargs = 1; req->in.args[0].size = sizeof(*arg); diff --git a/include/linux/fuse.h b/include/linux/fuse.h index d48282197696..265635dc9908 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -104,11 +104,14 @@ struct fuse_file_lock { /** * INIT request/reply flags + * + * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) #define FUSE_FILE_OPS (1 << 2) #define FUSE_ATOMIC_O_TRUNC (1 << 3) +#define FUSE_EXPORT_SUPPORT (1 << 4) #define FUSE_BIG_WRITES (1 << 5) /** -- cgit From 8f421c595a9145959d8aab09172743132abdffdb Mon Sep 17 00:00:00 2001 From: Arthur Jones Date: Fri, 25 Jul 2008 01:49:04 -0700 Subject: edac: i5100 new intel chipset driver Preliminary support for the Intel 5100 MCH. CE and UE errors are reported along with the current DIMM label information and other memory parameters. Reasons why this is preliminary: 1) This chip has 2 independent memory controllers which, for best perforance, use interleaved accesses to the DDR2 memory. This architecture does not map very well to the current edac data structures which depend on symmetric channel access to the interleaved data. Without core changes, the best I could do for now is to map both memory controllers to different csrows (first all ranks of controller 0, then all ranks of controller 1). Someone much more familiar with the edac core than I will probably need to come up with a more general data structure to handle the interleaving and de-interleaving of the two memory controllers. 2) I have not yet tackled the de-interleaving of the rank/controller address space into the physical address space of the CPU. There is nothing fundamentally missing, it is just ending up to be a lot of code, and I'd rather keep it separate for now, esp since it doesn't work yet... 3) The code depends on a particular i5100 chip select to DIMM mainboard chip select mapping. This mapping seems obvious to me in order to support dual and single ranked memory, but it is not unique and DIMM labels could be wrong on other mainboards. There is no way to query this mapping that I know of. 4) The code requires that the i5100 is in 32GB mode. Only 4 ranks per controller, 2 ranks per DIMM are supported. I do not have hardware (nor do I expect to have hardware anytime soon) for the 48GB (6 ranks per controller) mode. 5) The serial presence detect code should be broken out into a "real" i2c driver so that decode-dimms.pl can work. Signed-off-by: Arthur Jones Signed-off-by: Doug Thompson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/edac/Kconfig | 7 + drivers/edac/Makefile | 1 + drivers/edac/i5100_edac.c | 827 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 3 + 4 files changed, 838 insertions(+) create mode 100644 drivers/edac/i5100_edac.c (limited to 'include/linux') diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 6e6c3c4aea6b..5a11e3cbcae2 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -123,6 +123,13 @@ config EDAC_I5000 Support for error detection and correction the Intel Greekcreek/Blackford chipsets. +config EDAC_I5100 + tristate "Intel San Clemente MCH" + depends on EDAC_MM_EDAC && X86 && PCI + help + Support for error detection and correction the Intel + San Clemente MCH. + config EDAC_MPC85XX tristate "Freescale MPC85xx" depends on EDAC_MM_EDAC && FSL_SOC && MPC85xx diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 83807731d4a9..e5e9104b5520 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -19,6 +19,7 @@ endif obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o obj-$(CONFIG_EDAC_I5000) += i5000_edac.o +obj-$(CONFIG_EDAC_I5100) += i5100_edac.o obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o obj-$(CONFIG_EDAC_E752X) += e752x_edac.o obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c new file mode 100644 index 000000000000..43430bf70181 --- /dev/null +++ b/drivers/edac/i5100_edac.c @@ -0,0 +1,827 @@ +/* + * Intel 5100 Memory Controllers kernel module + * + * This file may be distributed under the terms of the + * GNU General Public License. + * + * This module is based on the following document: + * + * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet + * http://download.intel.com/design/chipsets/datashts/318378.pdf + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "edac_core.h" + +/* register addresses and bit field accessors... */ + +/* device 16, func 1 */ +#define I5100_MS 0x44 /* Memory Status Register */ +#define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */ +#define I5100_SPDDATA_RDO(a) ((a) >> 15 & 1) +#define I5100_SPDDATA_SBE(a) ((a) >> 13 & 1) +#define I5100_SPDDATA_BUSY(a) ((a) >> 12 & 1) +#define I5100_SPDDATA_DATA(a) ((a) & ((1 << 8) - 1)) +#define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */ +#define I5100_SPDCMD_DTI(a) (((a) & ((1 << 4) - 1)) << 28) +#define I5100_SPDCMD_CKOVRD(a) (((a) & 1) << 27) +#define I5100_SPDCMD_SA(a) (((a) & ((1 << 3) - 1)) << 24) +#define I5100_SPDCMD_BA(a) (((a) & ((1 << 8) - 1)) << 16) +#define I5100_SPDCMD_DATA(a) (((a) & ((1 << 8) - 1)) << 8) +#define I5100_SPDCMD_CMD(a) ((a) & 1) +#define I5100_TOLM 0x6c /* Top of Low Memory */ +#define I5100_TOLM_TOLM(a) ((a) >> 12 & ((1 << 4) - 1)) +#define I5100_MIR0 0x80 /* Memory Interleave Range 0 */ +#define I5100_MIR1 0x84 /* Memory Interleave Range 1 */ +#define I5100_AMIR_0 0x8c /* Adjusted Memory Interleave Range 0 */ +#define I5100_AMIR_1 0x90 /* Adjusted Memory Interleave Range 1 */ +#define I5100_MIR_LIMIT(a) ((a) >> 4 & ((1 << 12) - 1)) +#define I5100_MIR_WAY1(a) ((a) >> 1 & 1) +#define I5100_MIR_WAY0(a) ((a) & 1) +#define I5100_FERR_NF_MEM 0xa0 /* MC First Non Fatal Errors */ +#define I5100_FERR_NF_MEM_CHAN_INDX(a) ((a) >> 28 & 1) +#define I5100_FERR_NF_MEM_SPD_MASK (1 << 18) +#define I5100_FERR_NF_MEM_M16ERR_MASK (1 << 16) +#define I5100_FERR_NF_MEM_M15ERR_MASK (1 << 15) +#define I5100_FERR_NF_MEM_M14ERR_MASK (1 << 14) +#define I5100_FERR_NF_MEM_ +#define I5100_FERR_NF_MEM_ +#define I5100_FERR_NF_MEM_ANY_MASK \ + (I5100_FERR_NF_MEM_M16ERR_MASK | \ + I5100_FERR_NF_MEM_M15ERR_MASK | \ + I5100_FERR_NF_MEM_M14ERR_MASK) +#define I5100_FERR_NF_MEM_ANY(a) ((a) & I5100_FERR_NF_MEM_ANY_MASK) +#define I5100_NERR_NF_MEM 0xa4 /* MC Next Non-Fatal Errors */ +#define I5100_NERR_NF_MEM_ANY(a) I5100_FERR_NF_MEM_ANY(a) + +/* device 21 and 22, func 0 */ +#define I5100_MTR_0 0x154 /* Memory Technology Registers 0-3 */ +#define I5100_DMIR 0x15c /* DIMM Interleave Range */ +#define I5100_DMIR_LIMIT(a) ((a) >> 16 & ((1 << 11) - 1)) +#define I5100_DMIR_RANK(a, i) ((a) >> (4 * i) & ((1 << 2) - 1)) +#define I5100_MTR_4 0x1b0 /* Memory Technology Registers 4,5 */ +#define I5100_MTR_PRESENT(a) ((a) >> 10 & 1) +#define I5100_MTR_ETHROTTLE(a) ((a) >> 9 & 1) +#define I5100_MTR_WIDTH(a) ((a) >> 8 & 1) +#define I5100_MTR_NUMBANK(a) ((a) >> 6 & 1) +#define I5100_MTR_NUMROW(a) ((a) >> 2 & ((1 << 2) - 1)) +#define I5100_MTR_NUMCOL(a) ((a) & ((1 << 2) - 1)) +#define I5100_VALIDLOG 0x18c /* Valid Log Markers */ +#define I5100_VALIDLOG_REDMEMVALID(a) ((a) >> 2 & 1) +#define I5100_VALIDLOG_RECMEMVALID(a) ((a) >> 1 & 1) +#define I5100_VALIDLOG_NRECMEMVALID(a) ((a) & 1) +#define I5100_NRECMEMA 0x190 /* Non-Recoverable Memory Error Log Reg A */ +#define I5100_NRECMEMA_MERR(a) ((a) >> 15 & ((1 << 5) - 1)) +#define I5100_NRECMEMA_BANK(a) ((a) >> 12 & ((1 << 3) - 1)) +#define I5100_NRECMEMA_RANK(a) ((a) >> 8 & ((1 << 3) - 1)) +#define I5100_NRECMEMA_DM_BUF_ID(a) ((a) & ((1 << 8) - 1)) +#define I5100_NRECMEMB 0x194 /* Non-Recoverable Memory Error Log Reg B */ +#define I5100_NRECMEMB_CAS(a) ((a) >> 16 & ((1 << 13) - 1)) +#define I5100_NRECMEMB_RAS(a) ((a) & ((1 << 16) - 1)) +#define I5100_REDMEMA 0x198 /* Recoverable Memory Data Error Log Reg A */ +#define I5100_REDMEMA_SYNDROME(a) (a) +#define I5100_REDMEMB 0x19c /* Recoverable Memory Data Error Log Reg B */ +#define I5100_REDMEMB_ECC_LOCATOR(a) ((a) & ((1 << 18) - 1)) +#define I5100_RECMEMA 0x1a0 /* Recoverable Memory Error Log Reg A */ +#define I5100_RECMEMA_MERR(a) I5100_NRECMEMA_MERR(a) +#define I5100_RECMEMA_BANK(a) I5100_NRECMEMA_BANK(a) +#define I5100_RECMEMA_RANK(a) I5100_NRECMEMA_RANK(a) +#define I5100_RECMEMA_DM_BUF_ID(a) I5100_NRECMEMA_DM_BUF_ID(a) +#define I5100_RECMEMB 0x1a4 /* Recoverable Memory Error Log Reg B */ +#define I5100_RECMEMB_CAS(a) I5100_NRECMEMB_CAS(a) +#define I5100_RECMEMB_RAS(a) I5100_NRECMEMB_RAS(a) + +/* some generic limits */ +#define I5100_MAX_RANKS_PER_CTLR 6 +#define I5100_MAX_CTLRS 2 +#define I5100_MAX_RANKS_PER_DIMM 4 +#define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */ +#define I5100_MAX_DIMM_SLOTS_PER_CTLR 4 +#define I5100_MAX_RANK_INTERLEAVE 4 +#define I5100_MAX_DMIRS 5 + +struct i5100_priv { + /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */ + int dimm_numrank[I5100_MAX_CTLRS][I5100_MAX_DIMM_SLOTS_PER_CTLR]; + + /* + * mainboard chip select map -- maps i5100 chip selects to + * DIMM slot chip selects. In the case of only 4 ranks per + * controller, the mapping is fairly obvious but not unique. + * we map -1 -> NC and assume both controllers use the same + * map... + * + */ + int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CTLR][I5100_MAX_RANKS_PER_DIMM]; + + /* memory interleave range */ + struct { + u64 limit; + unsigned way[2]; + } mir[I5100_MAX_CTLRS]; + + /* adjusted memory interleave range register */ + unsigned amir[I5100_MAX_CTLRS]; + + /* dimm interleave range */ + struct { + unsigned rank[I5100_MAX_RANK_INTERLEAVE]; + u64 limit; + } dmir[I5100_MAX_CTLRS][I5100_MAX_DMIRS]; + + /* memory technology registers... */ + struct { + unsigned present; /* 0 or 1 */ + unsigned ethrottle; /* 0 or 1 */ + unsigned width; /* 4 or 8 bits */ + unsigned numbank; /* 2 or 3 lines */ + unsigned numrow; /* 13 .. 16 lines */ + unsigned numcol; /* 11 .. 12 lines */ + } mtr[I5100_MAX_CTLRS][I5100_MAX_RANKS_PER_CTLR]; + + u64 tolm; /* top of low memory in bytes */ + unsigned ranksperctlr; /* number of ranks per controller */ + + struct pci_dev *mc; /* device 16 func 1 */ + struct pci_dev *ch0mm; /* device 21 func 0 */ + struct pci_dev *ch1mm; /* device 22 func 0 */ +}; + +/* map a rank/ctlr to a slot number on the mainboard */ +static int i5100_rank_to_slot(const struct mem_ctl_info *mci, + int ctlr, int rank) +{ + const struct i5100_priv *priv = mci->pvt_info; + int i; + + for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) { + int j; + const int numrank = priv->dimm_numrank[ctlr][i]; + + for (j = 0; j < numrank; j++) + if (priv->dimm_csmap[i][j] == rank) + return i * 2 + ctlr; + } + + return -1; +} + +/* + * The processor bus memory addresses are broken into three + * pieces, whereas the controller addresses are contiguous. + * + * here we map from the controller address space to the + * processor address space: + * + * Processor Address Space + * +-----------------------------+ + * | | + * | "high" memory addresses | + * | | + * +-----------------------------+ <- 4GB on the i5100 + * | | + * | other non-memory addresses | + * | | + * +-----------------------------+ <- top of low memory + * | | + * | "low" memory addresses | + * | | + * +-----------------------------+ + */ +static unsigned long i5100_ctl_page_to_phys(struct mem_ctl_info *mci, + unsigned long cntlr_addr) +{ + const struct i5100_priv *priv = mci->pvt_info; + + if (cntlr_addr < priv->tolm) + return cntlr_addr; + + return (1ULL << 32) + (cntlr_addr - priv->tolm); +} + +static const char *i5100_err_msg(unsigned err) +{ + const char *merrs[] = { + "unknown", /* 0 */ + "uncorrectable data ECC on replay", /* 1 */ + "unknown", /* 2 */ + "unknown", /* 3 */ + "aliased uncorrectable demand data ECC", /* 4 */ + "aliased uncorrectable spare-copy data ECC", /* 5 */ + "aliased uncorrectable patrol data ECC", /* 6 */ + "unknown", /* 7 */ + "unknown", /* 8 */ + "unknown", /* 9 */ + "non-aliased uncorrectable demand data ECC", /* 10 */ + "non-aliased uncorrectable spare-copy data ECC", /* 11 */ + "non-aliased uncorrectable patrol data ECC", /* 12 */ + "unknown", /* 13 */ + "correctable demand data ECC", /* 14 */ + "correctable spare-copy data ECC", /* 15 */ + "correctable patrol data ECC", /* 16 */ + "unknown", /* 17 */ + "SPD protocol error", /* 18 */ + "unknown", /* 19 */ + "spare copy initiated", /* 20 */ + "spare copy completed", /* 21 */ + }; + unsigned i; + + for (i = 0; i < ARRAY_SIZE(merrs); i++) + if (1 << i & err) + return merrs[i]; + + return "none"; +} + +/* convert csrow index into a rank (per controller -- 0..5) */ +static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow) +{ + const struct i5100_priv *priv = mci->pvt_info; + + return csrow % priv->ranksperctlr; +} + +/* convert csrow index into a controller (0..1) */ +static int i5100_csrow_to_cntlr(const struct mem_ctl_info *mci, int csrow) +{ + const struct i5100_priv *priv = mci->pvt_info; + + return csrow / priv->ranksperctlr; +} + +static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci, + int ctlr, int rank) +{ + const struct i5100_priv *priv = mci->pvt_info; + + return ctlr * priv->ranksperctlr + rank; +} + +static void i5100_handle_ce(struct mem_ctl_info *mci, + int ctlr, + unsigned bank, + unsigned rank, + unsigned long syndrome, + unsigned cas, + unsigned ras, + const char *msg) +{ + const int csrow = i5100_rank_to_csrow(mci, ctlr, rank); + + printk(KERN_ERR + "CE ctlr %d, bank %u, rank %u, syndrome 0x%lx, " + "cas %u, ras %u, csrow %u, label \"%s\": %s\n", + ctlr, bank, rank, syndrome, cas, ras, + csrow, mci->csrows[csrow].channels[0].label, msg); + + mci->ce_count++; + mci->csrows[csrow].ce_count++; + mci->csrows[csrow].channels[0].ce_count++; +} + +static void i5100_handle_ue(struct mem_ctl_info *mci, + int ctlr, + unsigned bank, + unsigned rank, + unsigned long syndrome, + unsigned cas, + unsigned ras, + const char *msg) +{ + const int csrow = i5100_rank_to_csrow(mci, ctlr, rank); + + printk(KERN_ERR + "UE ctlr %d, bank %u, rank %u, syndrome 0x%lx, " + "cas %u, ras %u, csrow %u, label \"%s\": %s\n", + ctlr, bank, rank, syndrome, cas, ras, + csrow, mci->csrows[csrow].channels[0].label, msg); + + mci->ue_count++; + mci->csrows[csrow].ue_count++; +} + +static void i5100_read_log(struct mem_ctl_info *mci, int ctlr, + u32 ferr, u32 nerr) +{ + struct i5100_priv *priv = mci->pvt_info; + struct pci_dev *pdev = (ctlr) ? priv->ch1mm : priv->ch0mm; + u32 dw; + u32 dw2; + unsigned syndrome = 0; + unsigned ecc_loc = 0; + unsigned merr; + unsigned bank; + unsigned rank; + unsigned cas; + unsigned ras; + + pci_read_config_dword(pdev, I5100_VALIDLOG, &dw); + + if (I5100_VALIDLOG_REDMEMVALID(dw)) { + pci_read_config_dword(pdev, I5100_REDMEMA, &dw2); + syndrome = I5100_REDMEMA_SYNDROME(dw2); + pci_read_config_dword(pdev, I5100_REDMEMB, &dw2); + ecc_loc = I5100_REDMEMB_ECC_LOCATOR(dw2); + } + + if (I5100_VALIDLOG_RECMEMVALID(dw)) { + const char *msg; + + pci_read_config_dword(pdev, I5100_RECMEMA, &dw2); + merr = I5100_RECMEMA_MERR(dw2); + bank = I5100_RECMEMA_BANK(dw2); + rank = I5100_RECMEMA_RANK(dw2); + + pci_read_config_dword(pdev, I5100_RECMEMB, &dw2); + cas = I5100_RECMEMB_CAS(dw2); + ras = I5100_RECMEMB_RAS(dw2); + + /* FIXME: not really sure if this is what merr is... + */ + if (!merr) + msg = i5100_err_msg(ferr); + else + msg = i5100_err_msg(nerr); + + i5100_handle_ce(mci, ctlr, bank, rank, syndrome, cas, ras, msg); + } + + if (I5100_VALIDLOG_NRECMEMVALID(dw)) { + const char *msg; + + pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2); + merr = I5100_NRECMEMA_MERR(dw2); + bank = I5100_NRECMEMA_BANK(dw2); + rank = I5100_NRECMEMA_RANK(dw2); + + pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2); + cas = I5100_NRECMEMB_CAS(dw2); + ras = I5100_NRECMEMB_RAS(dw2); + + /* FIXME: not really sure if this is what merr is... + */ + if (!merr) + msg = i5100_err_msg(ferr); + else + msg = i5100_err_msg(nerr); + + i5100_handle_ue(mci, ctlr, bank, rank, syndrome, cas, ras, msg); + } + + pci_write_config_dword(pdev, I5100_VALIDLOG, dw); +} + +static void i5100_check_error(struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + u32 dw; + + + pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw); + if (I5100_FERR_NF_MEM_ANY(dw)) { + u32 dw2; + + pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2); + if (dw2) + pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM, + dw2); + pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw); + + i5100_read_log(mci, I5100_FERR_NF_MEM_CHAN_INDX(dw), + I5100_FERR_NF_MEM_ANY(dw), + I5100_NERR_NF_MEM_ANY(dw2)); + } +} + +static struct pci_dev *pci_get_device_func(unsigned vendor, + unsigned device, + unsigned func) +{ + struct pci_dev *ret = NULL; + + while (1) { + ret = pci_get_device(vendor, device, ret); + + if (!ret) + break; + + if (PCI_FUNC(ret->devfn) == func) + break; + } + + return ret; +} + +static unsigned long __devinit i5100_npages(struct mem_ctl_info *mci, + int csrow) +{ + struct i5100_priv *priv = mci->pvt_info; + const unsigned ctlr_rank = i5100_csrow_to_rank(mci, csrow); + const unsigned ctlr = i5100_csrow_to_cntlr(mci, csrow); + unsigned addr_lines; + + /* dimm present? */ + if (!priv->mtr[ctlr][ctlr_rank].present) + return 0ULL; + + addr_lines = + I5100_DIMM_ADDR_LINES + + priv->mtr[ctlr][ctlr_rank].numcol + + priv->mtr[ctlr][ctlr_rank].numrow + + priv->mtr[ctlr][ctlr_rank].numbank; + + return (unsigned long) + ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE); +} + +static void __devinit i5100_init_mtr(struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; + int i; + + for (i = 0; i < I5100_MAX_CTLRS; i++) { + int j; + struct pci_dev *pdev = mms[i]; + + for (j = 0; j < I5100_MAX_RANKS_PER_CTLR; j++) { + const unsigned addr = + (j < 4) ? I5100_MTR_0 + j * 2 : + I5100_MTR_4 + (j - 4) * 2; + u16 w; + + pci_read_config_word(pdev, addr, &w); + + priv->mtr[i][j].present = I5100_MTR_PRESENT(w); + priv->mtr[i][j].ethrottle = I5100_MTR_ETHROTTLE(w); + priv->mtr[i][j].width = 4 + 4 * I5100_MTR_WIDTH(w); + priv->mtr[i][j].numbank = 2 + I5100_MTR_NUMBANK(w); + priv->mtr[i][j].numrow = 13 + I5100_MTR_NUMROW(w); + priv->mtr[i][j].numcol = 10 + I5100_MTR_NUMCOL(w); + } + } +} + +/* + * FIXME: make this into a real i2c adapter (so that dimm-decode + * will work)? + */ +static int i5100_read_spd_byte(const struct mem_ctl_info *mci, + u8 ch, u8 slot, u8 addr, u8 *byte) +{ + struct i5100_priv *priv = mci->pvt_info; + u16 w; + u32 dw; + unsigned long et; + + pci_read_config_word(priv->mc, I5100_SPDDATA, &w); + if (I5100_SPDDATA_BUSY(w)) + return -1; + + dw = I5100_SPDCMD_DTI(0xa) | + I5100_SPDCMD_CKOVRD(1) | + I5100_SPDCMD_SA(ch * 4 + slot) | + I5100_SPDCMD_BA(addr) | + I5100_SPDCMD_DATA(0) | + I5100_SPDCMD_CMD(0); + pci_write_config_dword(priv->mc, I5100_SPDCMD, dw); + + /* wait up to 100ms */ + et = jiffies + HZ / 10; + udelay(100); + while (1) { + pci_read_config_word(priv->mc, I5100_SPDDATA, &w); + if (!I5100_SPDDATA_BUSY(w)) + break; + udelay(100); + } + + if (!I5100_SPDDATA_RDO(w) || I5100_SPDDATA_SBE(w)) + return -1; + + *byte = I5100_SPDDATA_DATA(w); + + return 0; +} + +/* + * fill dimm chip select map + * + * FIXME: + * o only valid for 4 ranks per controller + * o not the only way to may chip selects to dimm slots + * o investigate if there is some way to obtain this map from the bios + */ +static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + int i; + + WARN_ON(priv->ranksperctlr != 4); + + for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) { + int j; + + for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++) + priv->dimm_csmap[i][j] = -1; /* default NC */ + } + + /* only 2 chip selects per slot... */ + priv->dimm_csmap[0][0] = 0; + priv->dimm_csmap[0][1] = 3; + priv->dimm_csmap[1][0] = 1; + priv->dimm_csmap[1][1] = 2; + priv->dimm_csmap[2][0] = 2; + priv->dimm_csmap[3][0] = 3; +} + +static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev, + struct mem_ctl_info *mci) +{ + struct i5100_priv *priv = mci->pvt_info; + int i; + + for (i = 0; i < I5100_MAX_CTLRS; i++) { + int j; + + for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CTLR; j++) { + u8 rank; + + if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0) + priv->dimm_numrank[i][j] = 0; + else + priv->dimm_numrank[i][j] = (rank & 3) + 1; + } + } + + i5100_init_dimm_csmap(mci); +} + +static void __devinit i5100_init_interleaving(struct pci_dev *pdev, + struct mem_ctl_info *mci) +{ + u16 w; + u32 dw; + struct i5100_priv *priv = mci->pvt_info; + struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm }; + int i; + + pci_read_config_word(pdev, I5100_TOLM, &w); + priv->tolm = (u64) I5100_TOLM_TOLM(w) * 256 * 1024 * 1024; + + pci_read_config_word(pdev, I5100_MIR0, &w); + priv->mir[0].limit = (u64) I5100_MIR_LIMIT(w) << 28; + priv->mir[0].way[1] = I5100_MIR_WAY1(w); + priv->mir[0].way[0] = I5100_MIR_WAY0(w); + + pci_read_config_word(pdev, I5100_MIR1, &w); + priv->mir[1].limit = (u64) I5100_MIR_LIMIT(w) << 28; + priv->mir[1].way[1] = I5100_MIR_WAY1(w); + priv->mir[1].way[0] = I5100_MIR_WAY0(w); + + pci_read_config_word(pdev, I5100_AMIR_0, &w); + priv->amir[0] = w; + pci_read_config_word(pdev, I5100_AMIR_1, &w); + priv->amir[1] = w; + + for (i = 0; i < I5100_MAX_CTLRS; i++) { + int j; + + for (j = 0; j < 5; j++) { + int k; + + pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw); + + priv->dmir[i][j].limit = + (u64) I5100_DMIR_LIMIT(dw) << 28; + for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++) + priv->dmir[i][j].rank[k] = + I5100_DMIR_RANK(dw, k); + } + } + + i5100_init_mtr(mci); +} + +static void __devinit i5100_init_csrows(struct mem_ctl_info *mci) +{ + int i; + unsigned long total_pages = 0UL; + struct i5100_priv *priv = mci->pvt_info; + + for (i = 0; i < mci->nr_csrows; i++) { + const unsigned long npages = i5100_npages(mci, i); + const unsigned cntlr = i5100_csrow_to_cntlr(mci, i); + const unsigned rank = i5100_csrow_to_rank(mci, i); + + if (!npages) + continue; + + /* + * FIXME: these two are totally bogus -- I don't see how to + * map them correctly to this structure... + */ + mci->csrows[i].first_page = total_pages; + mci->csrows[i].last_page = total_pages + npages - 1; + mci->csrows[i].page_mask = 0UL; + + mci->csrows[i].nr_pages = npages; + mci->csrows[i].grain = 32; + mci->csrows[i].csrow_idx = i; + mci->csrows[i].dtype = + (priv->mtr[cntlr][rank].width == 4) ? DEV_X4 : DEV_X8; + mci->csrows[i].ue_count = 0; + mci->csrows[i].ce_count = 0; + mci->csrows[i].mtype = MEM_RDDR2; + mci->csrows[i].edac_mode = EDAC_SECDED; + mci->csrows[i].mci = mci; + mci->csrows[i].nr_channels = 1; + mci->csrows[i].channels[0].chan_idx = 0; + mci->csrows[i].channels[0].ce_count = 0; + mci->csrows[i].channels[0].csrow = mci->csrows + i; + snprintf(mci->csrows[i].channels[0].label, + sizeof(mci->csrows[i].channels[0].label), + "DIMM%u", i5100_rank_to_slot(mci, cntlr, rank)); + + total_pages += npages; + } +} + +static int __devinit i5100_init_one(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int rc; + struct mem_ctl_info *mci; + struct i5100_priv *priv; + struct pci_dev *ch0mm, *ch1mm; + int ret = 0; + u32 dw; + int ranksperch; + + if (PCI_FUNC(pdev->devfn) != 1) + return -ENODEV; + + rc = pci_enable_device(pdev); + if (rc < 0) { + ret = rc; + goto bail; + } + + /* figure out how many ranks, from strapped state of 48GB_Mode input */ + pci_read_config_dword(pdev, I5100_MS, &dw); + ranksperch = !!(dw & (1 << 8)) * 2 + 4; + + if (ranksperch != 4) { + /* FIXME: get 6 ranks / controller to work - need hw... */ + printk(KERN_INFO "i5100_edac: unsupported configuration.\n"); + ret = -ENODEV; + goto bail; + } + + /* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */ + ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_5100_21, 0); + if (!ch0mm) + return -ENODEV; + + rc = pci_enable_device(ch0mm); + if (rc < 0) { + ret = rc; + goto bail_ch0; + } + + /* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */ + ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_5100_22, 0); + if (!ch1mm) { + ret = -ENODEV; + goto bail_ch0; + } + + rc = pci_enable_device(ch1mm); + if (rc < 0) { + ret = rc; + goto bail_ch1; + } + + mci = edac_mc_alloc(sizeof(*priv), ranksperch * 2, 1, 0); + if (!mci) { + ret = -ENOMEM; + goto bail_ch1; + } + + mci->dev = &pdev->dev; + + priv = mci->pvt_info; + priv->ranksperctlr = ranksperch; + priv->mc = pdev; + priv->ch0mm = ch0mm; + priv->ch1mm = ch1mm; + + i5100_init_dimm_layout(pdev, mci); + i5100_init_interleaving(pdev, mci); + + mci->mtype_cap = MEM_FLAG_FB_DDR2; + mci->edac_ctl_cap = EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->mod_name = "i5100_edac.c"; + mci->mod_ver = "not versioned"; + mci->ctl_name = "i5100"; + mci->dev_name = pci_name(pdev); + mci->ctl_page_to_phys = i5100_ctl_page_to_phys; + + mci->edac_check = i5100_check_error; + + i5100_init_csrows(mci); + + /* this strange construction seems to be in every driver, dunno why */ + switch (edac_op_state) { + case EDAC_OPSTATE_POLL: + case EDAC_OPSTATE_NMI: + break; + default: + edac_op_state = EDAC_OPSTATE_POLL; + break; + } + + if (edac_mc_add_mc(mci)) { + ret = -ENODEV; + goto bail_mc; + } + + goto bail; + +bail_mc: + edac_mc_free(mci); + +bail_ch1: + pci_dev_put(ch1mm); + +bail_ch0: + pci_dev_put(ch0mm); + +bail: + return ret; +} + +static void __devexit i5100_remove_one(struct pci_dev *pdev) +{ + struct mem_ctl_info *mci; + struct i5100_priv *priv; + + mci = edac_mc_del_mc(&pdev->dev); + + if (!mci) + return; + + priv = mci->pvt_info; + pci_dev_put(priv->ch0mm); + pci_dev_put(priv->ch1mm); + + edac_mc_free(mci); +} + +static const struct pci_device_id i5100_pci_tbl[] __devinitdata = { + /* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, i5100_pci_tbl); + +static struct pci_driver i5100_driver = { + .name = KBUILD_BASENAME, + .probe = i5100_init_one, + .remove = __devexit_p(i5100_remove_one), + .id_table = i5100_pci_tbl, +}; + +static int __init i5100_init(void) +{ + int pci_rc; + + pci_rc = pci_register_driver(&i5100_driver); + + return (pci_rc < 0) ? pci_rc : 0; +} + +static void __exit i5100_exit(void) +{ + pci_unregister_driver(&i5100_driver); +} + +module_init(i5100_init); +module_exit(i5100_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR + ("Arthur Jones "); +MODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers"); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 119ae7b8f028..c3b1761aba26 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2400,6 +2400,9 @@ #define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30 #define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60 #define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f +#define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 +#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 +#define PCI_DEVICE_ID_INTEL_5100_22 0x65f6 #define PCI_DEVICE_ID_INTEL_5400_ERR 0x4030 #define PCI_DEVICE_ID_INTEL_5400_FBD0 0x4035 #define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 -- cgit From 7dcf2a9fced59e58e4694cdcf15850c01fdba89b Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 1 Jul 2008 19:27:16 +0300 Subject: remove dummy asm/kvm.h files This patch removes the dummy asm/kvm.h files on architectures not (yet) supporting KVM and uses the same conditional headers installation as already used for a.out.h . Also removed are superfluous install rules in the s390 and x86 Kbuild files (they are already in Kbuild.asm). Signed-off-by: Adrian Bunk Acked-by: Sam Ravnborg Signed-off-by: David Woodhouse --- include/asm-alpha/kvm.h | 6 ------ include/asm-arm/kvm.h | 6 ------ include/asm-avr32/kvm.h | 6 ------ include/asm-blackfin/kvm.h | 6 ------ include/asm-cris/kvm.h | 6 ------ include/asm-frv/kvm.h | 6 ------ include/asm-generic/Kbuild.asm | 2 ++ include/asm-h8300/kvm.h | 6 ------ include/asm-m32r/kvm.h | 6 ------ include/asm-m68k/kvm.h | 6 ------ include/asm-m68knommu/kvm.h | 6 ------ include/asm-mips/kvm.h | 6 ------ include/asm-mn10300/kvm.h | 6 ------ include/asm-parisc/kvm.h | 6 ------ include/asm-s390/Kbuild | 1 - include/asm-sh/kvm.h | 6 ------ include/asm-sparc/kvm.h | 6 ------ include/asm-sparc64/kvm.h | 1 - include/asm-um/kvm.h | 6 ------ include/asm-x86/Kbuild | 1 - include/asm-xtensa/kvm.h | 6 ------ include/linux/Kbuild | 2 ++ 22 files changed, 4 insertions(+), 105 deletions(-) delete mode 100644 include/asm-alpha/kvm.h delete mode 100644 include/asm-arm/kvm.h delete mode 100644 include/asm-avr32/kvm.h delete mode 100644 include/asm-blackfin/kvm.h delete mode 100644 include/asm-cris/kvm.h delete mode 100644 include/asm-frv/kvm.h delete mode 100644 include/asm-h8300/kvm.h delete mode 100644 include/asm-m32r/kvm.h delete mode 100644 include/asm-m68k/kvm.h delete mode 100644 include/asm-m68knommu/kvm.h delete mode 100644 include/asm-mips/kvm.h delete mode 100644 include/asm-mn10300/kvm.h delete mode 100644 include/asm-parisc/kvm.h delete mode 100644 include/asm-sh/kvm.h delete mode 100644 include/asm-sparc/kvm.h delete mode 100644 include/asm-sparc64/kvm.h delete mode 100644 include/asm-um/kvm.h delete mode 100644 include/asm-xtensa/kvm.h (limited to 'include/linux') diff --git a/include/asm-alpha/kvm.h b/include/asm-alpha/kvm.h deleted file mode 100644 index b9daec429689..000000000000 --- a/include/asm-alpha/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_ALPHA_H -#define __LINUX_KVM_ALPHA_H - -/* alpha does not support KVM */ - -#endif diff --git a/include/asm-arm/kvm.h b/include/asm-arm/kvm.h deleted file mode 100644 index cb3c08cbcb9e..000000000000 --- a/include/asm-arm/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_ARM_H -#define __LINUX_KVM_ARM_H - -/* arm does not support KVM */ - -#endif diff --git a/include/asm-avr32/kvm.h b/include/asm-avr32/kvm.h deleted file mode 100644 index 8c5777020e2c..000000000000 --- a/include/asm-avr32/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_AVR32_H -#define __LINUX_KVM_AVR32_H - -/* avr32 does not support KVM */ - -#endif diff --git a/include/asm-blackfin/kvm.h b/include/asm-blackfin/kvm.h deleted file mode 100644 index e3477d77c014..000000000000 --- a/include/asm-blackfin/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_BLACKFIN_H -#define __LINUX_KVM_BLACKFIN_H - -/* blackfin does not support KVM */ - -#endif diff --git a/include/asm-cris/kvm.h b/include/asm-cris/kvm.h deleted file mode 100644 index c860f51149f0..000000000000 --- a/include/asm-cris/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_CRIS_H -#define __LINUX_KVM_CRIS_H - -/* cris does not support KVM */ - -#endif diff --git a/include/asm-frv/kvm.h b/include/asm-frv/kvm.h deleted file mode 100644 index 9c8a4f08d0a9..000000000000 --- a/include/asm-frv/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_FRV_H -#define __LINUX_KVM_FRV_H - -/* frv does not support KVM */ - -#endif diff --git a/include/asm-generic/Kbuild.asm b/include/asm-generic/Kbuild.asm index 7cd25b8e7c9a..1170dc60e638 100644 --- a/include/asm-generic/Kbuild.asm +++ b/include/asm-generic/Kbuild.asm @@ -1,4 +1,6 @@ +ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/kvm.h),) header-y += kvm.h +endif ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/a.out.h),) unifdef-y += a.out.h diff --git a/include/asm-h8300/kvm.h b/include/asm-h8300/kvm.h deleted file mode 100644 index bdbed7b987e1..000000000000 --- a/include/asm-h8300/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_H8300_H -#define __LINUX_KVM_H8300_H - -/* h8300 does not support KVM */ - -#endif diff --git a/include/asm-m32r/kvm.h b/include/asm-m32r/kvm.h deleted file mode 100644 index 99a40515b77e..000000000000 --- a/include/asm-m32r/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_M32R_H -#define __LINUX_KVM_M32R_H - -/* m32r does not support KVM */ - -#endif diff --git a/include/asm-m68k/kvm.h b/include/asm-m68k/kvm.h deleted file mode 100644 index 7ed27fce5240..000000000000 --- a/include/asm-m68k/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_M68K_H -#define __LINUX_KVM_M68K_H - -/* m68k does not support KVM */ - -#endif diff --git a/include/asm-m68knommu/kvm.h b/include/asm-m68knommu/kvm.h deleted file mode 100644 index b49d4258dabb..000000000000 --- a/include/asm-m68knommu/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_M68KNOMMU_H -#define __LINUX_KVM_M68KNOMMU_H - -/* m68knommu does not support KVM */ - -#endif diff --git a/include/asm-mips/kvm.h b/include/asm-mips/kvm.h deleted file mode 100644 index 093a5b7f796b..000000000000 --- a/include/asm-mips/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_MIPS_H -#define __LINUX_KVM_MIPS_H - -/* mips does not support KVM */ - -#endif diff --git a/include/asm-mn10300/kvm.h b/include/asm-mn10300/kvm.h deleted file mode 100644 index f6b609ff4a57..000000000000 --- a/include/asm-mn10300/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_MN10300_H -#define __LINUX_KVM_MN10300_H - -/* mn10300 does not support KVM */ - -#endif diff --git a/include/asm-parisc/kvm.h b/include/asm-parisc/kvm.h deleted file mode 100644 index 00cc45812547..000000000000 --- a/include/asm-parisc/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_PARISC_H -#define __LINUX_KVM_PARISC_H - -/* parisc does not support KVM */ - -#endif diff --git a/include/asm-s390/Kbuild b/include/asm-s390/Kbuild index bb5e9edb9825..63a23415fba6 100644 --- a/include/asm-s390/Kbuild +++ b/include/asm-s390/Kbuild @@ -7,7 +7,6 @@ header-y += tape390.h header-y += ucontext.h header-y += vtoc.h header-y += zcrypt.h -header-y += kvm.h header-y += chsc.h unifdef-y += cmb.h diff --git a/include/asm-sh/kvm.h b/include/asm-sh/kvm.h deleted file mode 100644 index 6af51dbab2d0..000000000000 --- a/include/asm-sh/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_SH_H -#define __LINUX_KVM_SH_H - -/* sh does not support KVM */ - -#endif diff --git a/include/asm-sparc/kvm.h b/include/asm-sparc/kvm.h deleted file mode 100644 index 2e5478da3819..000000000000 --- a/include/asm-sparc/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_SPARC_H -#define __LINUX_KVM_SPARC_H - -/* sparc does not support KVM */ - -#endif diff --git a/include/asm-sparc64/kvm.h b/include/asm-sparc64/kvm.h deleted file mode 100644 index 53564ad86b15..000000000000 --- a/include/asm-sparc64/kvm.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-um/kvm.h b/include/asm-um/kvm.h deleted file mode 100644 index 66aa77094551..000000000000 --- a/include/asm-um/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_UM_H -#define __LINUX_KVM_UM_H - -/* um does not support KVM */ - -#endif diff --git a/include/asm-x86/Kbuild b/include/asm-x86/Kbuild index 1e3554596f72..811e9828ccb3 100644 --- a/include/asm-x86/Kbuild +++ b/include/asm-x86/Kbuild @@ -3,7 +3,6 @@ include include/asm-generic/Kbuild.asm header-y += boot.h header-y += bootparam.h header-y += debugreg.h -header-y += kvm.h header-y += ldt.h header-y += msr-index.h header-y += prctl.h diff --git a/include/asm-xtensa/kvm.h b/include/asm-xtensa/kvm.h deleted file mode 100644 index bda4e331e98c..000000000000 --- a/include/asm-xtensa/kvm.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __LINUX_KVM_XTENSA_H -#define __LINUX_KVM_XTENSA_H - -/* xtensa does not support KVM */ - -#endif diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 71d70d1fbce2..402c8f55d713 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -256,7 +256,9 @@ unifdef-y += kd.h unifdef-y += kernelcapi.h unifdef-y += kernel.h unifdef-y += keyboard.h +ifneq ($(wildcard $(srctree)/include/asm-$(SRCARCH)/kvm.h),) unifdef-y += kvm.h +endif unifdef-y += llc.h unifdef-y += loop.h unifdef-y += lp.h -- cgit From c6af5e9f8a57467df2e55e428316a43480174521 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Fri, 25 Jul 2008 15:48:04 +0200 Subject: bootmem: Move node allocation macros back to !HAVE_ARCH_BOOTMEM_NODE These got unintentionally moved, put them back as x86 provides its own versions. Signed-off-by: Johannes Weiner Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 4ddf2922fc8d..652470b687c9 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -103,17 +103,16 @@ extern void *__alloc_bootmem_low_node(pg_data_t *pgdat, __alloc_bootmem(x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low_pages(x) \ __alloc_bootmem_low(x, PAGE_SIZE, 0) -#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ - -extern int reserve_bootmem_generic(unsigned long addr, unsigned long size, - int flags); - #define alloc_bootmem_node(pgdat, x) \ __alloc_bootmem_node(pgdat, x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_pages_node(pgdat, x) \ __alloc_bootmem_node(pgdat, x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low_pages_node(pgdat, x) \ __alloc_bootmem_low_node(pgdat, x, PAGE_SIZE, 0) +#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ + +extern int reserve_bootmem_generic(unsigned long addr, unsigned long size, + int flags); extern void *alloc_bootmem_section(unsigned long size, unsigned long section_nr); -- cgit From b4615e69b6c6353878b734a8202b65efbc554df4 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Fri, 25 Jul 2008 13:19:22 -0700 Subject: sys_paccept definition missing __user annotation Introduced by commit aaca0bdca573f3f51ea03139f9c7289541e7bca3 ("flag parameters: paccept"): net/socket.c:1515:17: error: symbol 'sys_paccept' redeclared with different type (originally declared at include/linux/syscalls.h:413) - incompatible argument 4 (different address spaces) Signed-off-by: Harvey Harrison Signed-off-by: Linus Torvalds --- include/linux/syscalls.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 06f2bf76c030..d6ff145919ca 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -411,7 +411,7 @@ asmlinkage long sys_bind(int, struct sockaddr __user *, int); asmlinkage long sys_connect(int, struct sockaddr __user *, int); asmlinkage long sys_accept(int, struct sockaddr __user *, int __user *); asmlinkage long sys_paccept(int, struct sockaddr __user *, int __user *, - const sigset_t *, size_t, int); + const __user sigset_t *, size_t, int); asmlinkage long sys_getsockname(int, struct sockaddr __user *, int __user *); asmlinkage long sys_getpeername(int, struct sockaddr __user *, int __user *); asmlinkage long sys_send(int, void __user *, size_t, unsigned); -- cgit From 3f07af494dfa6de43137dae430431c9fbf929c0c Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 25 Jul 2008 22:25:13 -0400 Subject: of: adapt of_find_i2c_driver() to be usable by SPI also SPI has a similar problem as I2C in that it needs to determine an appropriate modalias value for each device node. This patch adapts the of_i2c of_find_i2c_driver() function to be usable by of_spi also. Signed-off-by: Grant Likely --- drivers/of/base.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/of/of_i2c.c | 64 ++------------------------------------ include/linux/of.h | 1 + 3 files changed, 92 insertions(+), 61 deletions(-) (limited to 'include/linux') diff --git a/drivers/of/base.c b/drivers/of/base.c index 23ffb7c0caf2..ad8ac1a8af28 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -385,3 +385,91 @@ struct device_node *of_find_matching_node(struct device_node *from, return np; } EXPORT_SYMBOL(of_find_matching_node); + +/** + * of_modalias_table: Table of explicit compatible ==> modalias mappings + * + * This table allows particulare compatible property values to be mapped + * to modalias strings. This is useful for busses which do not directly + * understand the OF device tree but are populated based on data contained + * within the device tree. SPI and I2C are the two current users of this + * table. + * + * In most cases, devices do not need to be listed in this table because + * the modalias value can be derived directly from the compatible table. + * However, if for any reason a value cannot be derived, then this table + * provides a method to override the implicit derivation. + * + * At the moment, a single table is used for all bus types because it is + * assumed that the data size is small and that the compatible values + * should already be distinct enough to differentiate between SPI, I2C + * and other devices. + */ +struct of_modalias_table { + char *of_device; + char *modalias; +}; +static struct of_modalias_table of_modalias_table[] = { + /* Empty for now; add entries as needed */ +}; + +/** + * of_modalias_node - Lookup appropriate modalias for a device node + * @node: pointer to a device tree node + * @modalias: Pointer to buffer that modalias value will be copied into + * @len: Length of modalias value + * + * Based on the value of the compatible property, this routine will determine + * an appropriate modalias value for a particular device tree node. Three + * separate methods are used to derive a modalias value. + * + * First method is to lookup the compatible value in of_modalias_table. + * Second is to look for a "linux," entry in the compatible list + * and used that for modalias. Third is to strip off the manufacturer + * prefix from the first compatible entry and use the remainder as modalias + * + * This routine returns 0 on success + */ +int of_modalias_node(struct device_node *node, char *modalias, int len) +{ + int i, cplen; + const char *compatible; + const char *p; + + /* 1. search for exception list entry */ + for (i = 0; i < ARRAY_SIZE(of_modalias_table); i++) { + compatible = of_modalias_table[i].of_device; + if (!of_device_is_compatible(node, compatible)) + continue; + strlcpy(modalias, of_modalias_table[i].modalias, len); + return 0; + } + + compatible = of_get_property(node, "compatible", &cplen); + if (!compatible) + return -ENODEV; + + /* 2. search for linux, entry */ + p = compatible; + while (cplen > 0) { + if (!strncmp(p, "linux,", 6)) { + p += 6; + strlcpy(modalias, p, len); + return 0; + } + + i = strlen(p) + 1; + p += i; + cplen -= i; + } + + /* 3. take first compatible entry and strip manufacturer */ + p = strchr(compatible, ','); + if (!p) + return -ENODEV; + p++; + strlcpy(modalias, p, len); + return 0; +} +EXPORT_SYMBOL_GPL(of_modalias_node); + diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c index 344e1b03dd8b..6a98dc8aa30b 100644 --- a/drivers/of/of_i2c.c +++ b/drivers/of/of_i2c.c @@ -16,62 +16,6 @@ #include #include -struct i2c_driver_device { - char *of_device; - char *i2c_type; -}; - -static struct i2c_driver_device i2c_devices[] = { -}; - -static int of_find_i2c_driver(struct device_node *node, - struct i2c_board_info *info) -{ - int i, cplen; - const char *compatible; - const char *p; - - /* 1. search for exception list entry */ - for (i = 0; i < ARRAY_SIZE(i2c_devices); i++) { - if (!of_device_is_compatible(node, i2c_devices[i].of_device)) - continue; - if (strlcpy(info->type, i2c_devices[i].i2c_type, - I2C_NAME_SIZE) >= I2C_NAME_SIZE) - return -ENOMEM; - - return 0; - } - - compatible = of_get_property(node, "compatible", &cplen); - if (!compatible) - return -ENODEV; - - /* 2. search for linux, entry */ - p = compatible; - while (cplen > 0) { - if (!strncmp(p, "linux,", 6)) { - p += 6; - if (strlcpy(info->type, p, - I2C_NAME_SIZE) >= I2C_NAME_SIZE) - return -ENOMEM; - return 0; - } - - i = strlen(p) + 1; - p += i; - cplen -= i; - } - - /* 3. take fist compatible entry and strip manufacturer */ - p = strchr(compatible, ','); - if (!p) - return -ENODEV; - p++; - if (strlcpy(info->type, p, I2C_NAME_SIZE) >= I2C_NAME_SIZE) - return -ENOMEM; - return 0; -} - void of_register_i2c_devices(struct i2c_adapter *adap, struct device_node *adap_node) { @@ -83,6 +27,9 @@ void of_register_i2c_devices(struct i2c_adapter *adap, const u32 *addr; int len; + if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) + continue; + addr = of_get_property(node, "reg", &len); if (!addr || len < sizeof(int) || *addr > (1 << 10) - 1) { printk(KERN_ERR @@ -92,11 +39,6 @@ void of_register_i2c_devices(struct i2c_adapter *adap, info.irq = irq_of_parse_and_map(node, 0); - if (of_find_i2c_driver(node, &info) < 0) { - irq_dispose_mapping(info.irq); - continue; - } - info.addr = *addr; request_module(info.type); diff --git a/include/linux/of.h b/include/linux/of.h index 59a61bdc98b6..79886ade070f 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -70,5 +70,6 @@ extern int of_n_addr_cells(struct device_node *np); extern int of_n_size_cells(struct device_node *np); extern const struct of_device_id *of_match_node( const struct of_device_id *matches, const struct device_node *node); +extern int of_modalias_node(struct device_node *node, char *modalias, int len); #endif /* _LINUX_OF_H */ -- cgit From dc87c98e8f635a718f1abb2c3e15fc77c0001651 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 15 May 2008 16:50:22 -0600 Subject: spi: split up spi_new_device() to allow two stage registration. spi_new_device() allocates and registers an spi device all in one swoop. If the driver needs to add extra data to the spi_device before it is registered, then this causes problems. This is needed for OF device tree support so that the SPI device tree helper can add a pointer to the device node after the device is allocated, but before the device is registered. OF aware SPI devices can then retrieve data out of the device node to populate a platform data structure. This patch splits the allocation and registration portions of code out of spi_new_device() and creates two new functions; spi_alloc_device() and spi_register_device(). spi_new_device() is modified to use the new functions for allocation and registration. None of the existing users of spi_new_device() should be affected by this change. Drivers using the new API can forego the use of spi_board_info structure to describe the device layout and populate data into the spi_device structure directly. This change is in preparation for adding an OF device tree parser to generate spi_devices based on data in the device tree. Signed-off-by: Grant Likely Acked-by: David Brownell --- drivers/spi/spi.c | 139 +++++++++++++++++++++++++++++++++--------------- include/linux/spi/spi.h | 12 +++++ 2 files changed, 107 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ecca4a6a6f94..964124b60db2 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -178,6 +178,96 @@ struct boardinfo { static LIST_HEAD(board_list); static DEFINE_MUTEX(board_lock); +/** + * spi_alloc_device - Allocate a new SPI device + * @master: Controller to which device is connected + * Context: can sleep + * + * Allows a driver to allocate and initialize a spi_device without + * registering it immediately. This allows a driver to directly + * fill the spi_device with device parameters before calling + * spi_add_device() on it. + * + * Caller is responsible to call spi_add_device() on the returned + * spi_device structure to add it to the SPI master. If the caller + * needs to discard the spi_device without adding it, then it should + * call spi_dev_put() on it. + * + * Returns a pointer to the new device, or NULL. + */ +struct spi_device *spi_alloc_device(struct spi_master *master) +{ + struct spi_device *spi; + struct device *dev = master->dev.parent; + + if (!spi_master_get(master)) + return NULL; + + spi = kzalloc(sizeof *spi, GFP_KERNEL); + if (!spi) { + dev_err(dev, "cannot alloc spi_device\n"); + spi_master_put(master); + return NULL; + } + + spi->master = master; + spi->dev.parent = dev; + spi->dev.bus = &spi_bus_type; + spi->dev.release = spidev_release; + device_initialize(&spi->dev); + return spi; +} +EXPORT_SYMBOL_GPL(spi_alloc_device); + +/** + * spi_add_device - Add spi_device allocated with spi_alloc_device + * @spi: spi_device to register + * + * Companion function to spi_alloc_device. Devices allocated with + * spi_alloc_device can be added onto the spi bus with this function. + * + * Returns 0 on success; non-zero on failure + */ +int spi_add_device(struct spi_device *spi) +{ + struct device *dev = spi->master->dev.parent; + int status; + + /* Chipselects are numbered 0..max; validate. */ + if (spi->chip_select >= spi->master->num_chipselect) { + dev_err(dev, "cs%d >= max %d\n", + spi->chip_select, + spi->master->num_chipselect); + return -EINVAL; + } + + /* Set the bus ID string */ + snprintf(spi->dev.bus_id, sizeof spi->dev.bus_id, + "%s.%u", spi->master->dev.bus_id, + spi->chip_select); + + /* drivers may modify this initial i/o setup */ + status = spi->master->setup(spi); + if (status < 0) { + dev_err(dev, "can't %s %s, status %d\n", + "setup", spi->dev.bus_id, status); + return status; + } + + /* driver core catches callers that misbehave by defining + * devices that already exist. + */ + status = device_add(&spi->dev); + if (status < 0) { + dev_err(dev, "can't %s %s, status %d\n", + "add", spi->dev.bus_id, status); + return status; + } + + dev_dbg(dev, "registered child %s\n", spi->dev.bus_id); + return 0; +} +EXPORT_SYMBOL_GPL(spi_add_device); /** * spi_new_device - instantiate one new SPI device @@ -197,7 +287,6 @@ struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip) { struct spi_device *proxy; - struct device *dev = master->dev.parent; int status; /* NOTE: caller did any chip->bus_num checks necessary. @@ -207,66 +296,28 @@ struct spi_device *spi_new_device(struct spi_master *master, * suggests syslogged diagnostics are best here (ugh). */ - /* Chipselects are numbered 0..max; validate. */ - if (chip->chip_select >= master->num_chipselect) { - dev_err(dev, "cs%d > max %d\n", - chip->chip_select, - master->num_chipselect); - return NULL; - } - - if (!spi_master_get(master)) + proxy = spi_alloc_device(master); + if (!proxy) return NULL; WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); - proxy = kzalloc(sizeof *proxy, GFP_KERNEL); - if (!proxy) { - dev_err(dev, "can't alloc dev for cs%d\n", - chip->chip_select); - goto fail; - } - proxy->master = master; proxy->chip_select = chip->chip_select; proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; proxy->irq = chip->irq; strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); - - snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id, - "%s.%u", master->dev.bus_id, - chip->chip_select); - proxy->dev.parent = dev; - proxy->dev.bus = &spi_bus_type; proxy->dev.platform_data = (void *) chip->platform_data; proxy->controller_data = chip->controller_data; proxy->controller_state = NULL; - proxy->dev.release = spidev_release; - /* drivers may modify this initial i/o setup */ - status = master->setup(proxy); + status = spi_add_device(proxy); if (status < 0) { - dev_err(dev, "can't %s %s, status %d\n", - "setup", proxy->dev.bus_id, status); - goto fail; + spi_dev_put(proxy); + return NULL; } - /* driver core catches callers that misbehave by defining - * devices that already exist. - */ - status = device_register(&proxy->dev); - if (status < 0) { - dev_err(dev, "can't %s %s, status %d\n", - "add", proxy->dev.bus_id, status); - goto fail; - } - dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id); return proxy; - -fail: - spi_master_put(master); - kfree(proxy); - return NULL; } EXPORT_SYMBOL_GPL(spi_new_device); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index a9cc29d46653..4be01bb44377 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -778,7 +778,19 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) * use spi_new_device() to describe each device. You can also call * spi_unregister_device() to start making that device vanish, but * normally that would be handled by spi_unregister_master(). + * + * You can also use spi_alloc_device() and spi_add_device() to use a two + * stage registration sequence for each spi_device. This gives the caller + * some more control over the spi_device structure before it is registered, + * but requires that caller to initialize fields that would otherwise + * be defined using the board info. */ +extern struct spi_device * +spi_alloc_device(struct spi_master *master); + +extern int +spi_add_device(struct spi_device *spi); + extern struct spi_device * spi_new_device(struct spi_master *, struct spi_board_info *); -- cgit From 284b01897340974000bcc84de87a4e1becc8a83d Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 16 May 2008 11:37:09 -0600 Subject: spi: Add OF binding support for SPI busses This patch adds support for populating an SPI bus based on data in the OF device tree. This is useful for powerpc platforms which use the device tree instead of discrete code for describing platform layout. Signed-off-by: Grant Likely --- drivers/of/Kconfig | 6 ++++ drivers/of/Makefile | 1 + drivers/of/of_spi.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_spi.h | 18 ++++++++++ 4 files changed, 118 insertions(+) create mode 100644 drivers/of/of_spi.c create mode 100644 include/linux/of_spi.h (limited to 'include/linux') diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 1d7ec3129349..f821dbc952a4 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -13,3 +13,9 @@ config OF_I2C depends on PPC_OF && I2C help OpenFirmware I2C accessors + +config OF_SPI + def_tristate SPI + depends on OF && PPC_OF && SPI + help + OpenFirmware SPI accessors diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 548772e871fd..4c3c6f8e36f5 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -2,3 +2,4 @@ obj-y = base.o obj-$(CONFIG_OF_DEVICE) += device.o platform.o obj-$(CONFIG_OF_GPIO) += gpio.o obj-$(CONFIG_OF_I2C) += of_i2c.o +obj-$(CONFIG_OF_SPI) += of_spi.o diff --git a/drivers/of/of_spi.c b/drivers/of/of_spi.c new file mode 100644 index 000000000000..b01eec026f68 --- /dev/null +++ b/drivers/of/of_spi.c @@ -0,0 +1,93 @@ +/* + * 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 + +/** + * of_register_spi_devices - Register child devices onto the SPI bus + * @master: Pointer to spi_master device + * @np: parent node of SPI device nodes + * + * Registers an spi_device for each child node of 'np' which has a 'reg' + * property. + */ +void of_register_spi_devices(struct spi_master *master, struct device_node *np) +{ + struct spi_device *spi; + struct device_node *nc; + const u32 *prop; + int rc; + int len; + + for_each_child_of_node(np, 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 = *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; + + /* 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 = *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.archdata.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/include/linux/of_spi.h b/include/linux/of_spi.h new file mode 100644 index 000000000000..5f71ee8c0868 --- /dev/null +++ b/include/linux/of_spi.h @@ -0,0 +1,18 @@ +/* + * 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 +#include + +extern void of_register_spi_devices(struct spi_master *master, + struct device_node *np); + +#endif /* __LINUX_OF_SPI */ -- cgit From ec34c702ca8b7d6f0aa54379c3b0d0ec10b8ff23 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Fri, 25 Jul 2008 21:45:49 -0700 Subject: net: drop unused BUG_TRAP() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index f4d386c191f5..ca643b13b026 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -755,13 +755,6 @@ extern void __rtnl_unlock(void); } \ } while(0) -#define BUG_TRAP(x) do { \ - if (unlikely(!(x))) { \ - printk(KERN_ERR "KERNEL: assertion (%s) failed at %s (%d)\n", \ - #x, __FILE__ , __LINE__); \ - } \ -} while(0) - static inline u32 rtm_get_table(struct rtattr **rta, u8 table) { return RTA_GET_U32(rta[RTA_TABLE-1]); -- cgit From 36ac26171afa8dbf29226199699fe955d4a0b6f6 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sat, 26 Jul 2008 11:22:33 +0200 Subject: crashdump: fix undefined reference to `elfcorehdr_addr' fix build bug introduced by 95b68dec0d5 "calgary iommu: use the first kernels TCE tables in kdump": arch/x86/kernel/built-in.o: In function `calgary_iommu_init': (.init.text+0x8399): undefined reference to `elfcorehdr_addr' arch/x86/kernel/built-in.o: In function `calgary_iommu_init': (.init.text+0x856c): undefined reference to `elfcorehdr_addr' arch/x86/kernel/built-in.o: In function `detect_calgary': (.init.text+0x8c68): undefined reference to `elfcorehdr_addr' arch/x86/kernel/built-in.o: In function `detect_calgary': (.init.text+0x8d0c): undefined reference to `elfcorehdr_addr' make elfcorehdr_addr a generally available symbol. Signed-off-by: Ingo Molnar --- include/linux/crash_dump.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/crash_dump.h b/include/linux/crash_dump.h index 6cd39a927e1f..025e4f575103 100644 --- a/include/linux/crash_dump.h +++ b/include/linux/crash_dump.h @@ -8,7 +8,13 @@ #include #define ELFCORE_ADDR_MAX (-1ULL) + +#ifdef CONFIG_PROC_VMCORE extern unsigned long long elfcorehdr_addr; +#else +static const unsigned long long elfcorehdr_addr = ELFCORE_ADDR_MAX; +#endif + extern ssize_t copy_oldmem_page(unsigned long, char *, size_t, unsigned long, int); extern const struct file_operations proc_vmcore_operations; -- cgit From 3bc9f79ee1ddc913be0a6d3592036683ef8a3148 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 25 Jul 2008 14:57:58 +0200 Subject: iommu: add iommu_num_pages helper function Calculating the number of pages from given address and length numbers is a task required in multiple IOMMU implementations. So implement this as a generic function into the IOMMU helper code. Signed-off-by: Joerg Roedel Cc: iommu@lists.linux-foundation.org Cc: bhavna.sarathy@amd.com Cc: robert.richter@amd.com Cc: FUJITA Tomonori Signed-off-by: Ingo Molnar --- include/linux/iommu-helper.h | 1 + lib/iommu-helper.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/iommu-helper.h b/include/linux/iommu-helper.h index c975caf75385..f8598f583944 100644 --- a/include/linux/iommu-helper.h +++ b/include/linux/iommu-helper.h @@ -8,3 +8,4 @@ extern unsigned long iommu_area_alloc(unsigned long *map, unsigned long size, unsigned long align_mask); extern void iommu_area_free(unsigned long *map, unsigned long start, unsigned int nr); +extern unsigned long iommu_num_pages(unsigned long addr, unsigned long len); diff --git a/lib/iommu-helper.c b/lib/iommu-helper.c index a3b8d4c3f77a..889ddce2021e 100644 --- a/lib/iommu-helper.c +++ b/lib/iommu-helper.c @@ -80,3 +80,11 @@ void iommu_area_free(unsigned long *map, unsigned long start, unsigned int nr) } } EXPORT_SYMBOL(iommu_area_free); + +unsigned long iommu_num_pages(unsigned long addr, unsigned long len) +{ + unsigned long size = roundup((addr & ~PAGE_MASK) + len, PAGE_SIZE); + + return size >> PAGE_SHIFT; +} +EXPORT_SYMBOL(iommu_num_pages); -- cgit From b8d317d10cca76cabe6b03ebfeb23cc99118b731 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Thu, 24 Jul 2008 18:21:29 -0700 Subject: cpumask: make cpumask_of_cpu_map generic If an arch doesn't define cpumask_of_cpu_map, create a generic statically-initialized one for them. This allows removal of the buggy cpumask_of_cpu() macro (&cpumask_of_cpu() gives address of out-of-scope var). An arch with NR_CPUS of 4096 probably wants to allocate this itself based on the actual number of CPUs, since otherwise they're using 2MB of rodata (1024 cpus means 128k). That's what CONFIG_HAVE_CPUMASK_OF_CPU_MAP is for (only x86/64 does so at the moment). In future as we support more CPUs, we'll need to resort to a get_cpu_map()/put_cpu_map() allocation scheme. Signed-off-by: Mike Travis Signed-off-by: Rusty Russell Cc: Andrew Morton Cc: Jack Steiner Signed-off-by: Ingo Molnar --- include/linux/cpumask.h | 41 ++---------------- kernel/cpu.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 1b5c98e7fef7..8fa3b6d4a320 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -62,15 +62,7 @@ * int next_cpu_nr(cpu, mask) Next cpu past 'cpu', or nr_cpu_ids * * cpumask_t cpumask_of_cpu(cpu) Return cpumask with bit 'cpu' set - *ifdef CONFIG_HAS_CPUMASK_OF_CPU - * cpumask_of_cpu_ptr_declare(v) Declares cpumask_t *v - * cpumask_of_cpu_ptr_next(v, cpu) Sets v = &cpumask_of_cpu_map[cpu] - * cpumask_of_cpu_ptr(v, cpu) Combines above two operations - *else - * cpumask_of_cpu_ptr_declare(v) Declares cpumask_t _v and *v = &_v - * cpumask_of_cpu_ptr_next(v, cpu) Sets _v = cpumask_of_cpu(cpu) - * cpumask_of_cpu_ptr(v, cpu) Combines above two operations - *endif + * (can be used as an lvalue) * CPU_MASK_ALL Initializer - all bits set * CPU_MASK_NONE Initializer - no bits set * unsigned long *cpus_addr(mask) Array of unsigned long's in mask @@ -274,36 +266,9 @@ static inline void __cpus_shift_left(cpumask_t *dstp, } -#ifdef CONFIG_HAVE_CPUMASK_OF_CPU_MAP -extern cpumask_t *cpumask_of_cpu_map; +/* cpumask_of_cpu_map[] is in kernel/cpu.c */ +extern const cpumask_t *cpumask_of_cpu_map; #define cpumask_of_cpu(cpu) (cpumask_of_cpu_map[cpu]) -#define cpumask_of_cpu_ptr(v, cpu) \ - const cpumask_t *v = &cpumask_of_cpu(cpu) -#define cpumask_of_cpu_ptr_declare(v) \ - const cpumask_t *v -#define cpumask_of_cpu_ptr_next(v, cpu) \ - v = &cpumask_of_cpu(cpu) -#else -#define cpumask_of_cpu(cpu) \ -({ \ - typeof(_unused_cpumask_arg_) m; \ - if (sizeof(m) == sizeof(unsigned long)) { \ - m.bits[0] = 1UL<<(cpu); \ - } else { \ - cpus_clear(m); \ - cpu_set((cpu), m); \ - } \ - m; \ -}) -#define cpumask_of_cpu_ptr(v, cpu) \ - cpumask_t _##v = cpumask_of_cpu(cpu); \ - const cpumask_t *v = &_##v -#define cpumask_of_cpu_ptr_declare(v) \ - cpumask_t _##v; \ - const cpumask_t *v = &_##v -#define cpumask_of_cpu_ptr_next(v, cpu) \ - _##v = cpumask_of_cpu(cpu) -#endif #define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS) diff --git a/kernel/cpu.c b/kernel/cpu.c index 10ba5f1004a5..fe31ff3d3809 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -461,3 +461,112 @@ out: #endif /* CONFIG_PM_SLEEP_SMP */ #endif /* CONFIG_SMP */ + +#ifndef CONFIG_HAVE_CPUMASK_OF_CPU_MAP +/* 64 bits of zeros, for initializers. */ +#if BITS_PER_LONG == 32 +#define Z64 0, 0 +#else +#define Z64 0 +#endif + +/* Initializer macros. */ +#define CMI0(n) { .bits = { 1UL << (n) } } +#define CMI(n, ...) { .bits = { __VA_ARGS__, 1UL << ((n) % BITS_PER_LONG) } } + +#define CMI8(n, ...) \ + CMI((n), __VA_ARGS__), CMI((n)+1, __VA_ARGS__), \ + CMI((n)+2, __VA_ARGS__), CMI((n)+3, __VA_ARGS__), \ + CMI((n)+4, __VA_ARGS__), CMI((n)+5, __VA_ARGS__), \ + CMI((n)+6, __VA_ARGS__), CMI((n)+7, __VA_ARGS__) + +#if BITS_PER_LONG == 32 +#define CMI64(n, ...) \ + CMI8((n), __VA_ARGS__), CMI8((n)+8, __VA_ARGS__), \ + CMI8((n)+16, __VA_ARGS__), CMI8((n)+24, __VA_ARGS__), \ + CMI8((n)+32, 0, __VA_ARGS__), CMI8((n)+40, 0, __VA_ARGS__), \ + CMI8((n)+48, 0, __VA_ARGS__), CMI8((n)+56, 0, __VA_ARGS__) +#else +#define CMI64(n, ...) \ + CMI8((n), __VA_ARGS__), CMI8((n)+8, __VA_ARGS__), \ + CMI8((n)+16, __VA_ARGS__), CMI8((n)+24, __VA_ARGS__), \ + CMI8((n)+32, __VA_ARGS__), CMI8((n)+40, __VA_ARGS__), \ + CMI8((n)+48, __VA_ARGS__), CMI8((n)+56, __VA_ARGS__) +#endif + +#define CMI256(n, ...) \ + CMI64((n), __VA_ARGS__), CMI64((n)+64, Z64, __VA_ARGS__), \ + CMI64((n)+128, Z64, Z64, __VA_ARGS__), \ + CMI64((n)+192, Z64, Z64, Z64, __VA_ARGS__) +#define Z256 Z64, Z64, Z64, Z64 + +#define CMI1024(n, ...) \ + CMI256((n), __VA_ARGS__), \ + CMI256((n)+256, Z256, __VA_ARGS__), \ + CMI256((n)+512, Z256, Z256, __VA_ARGS__), \ + CMI256((n)+768, Z256, Z256, Z256, __VA_ARGS__) +#define Z1024 Z256, Z256, Z256, Z256 + +/* We want this statically initialized, just to be safe. We try not + * to waste too much space, either. */ +static const cpumask_t cpumask_map[] = { + CMI0(0), CMI0(1), CMI0(2), CMI0(3), +#if NR_CPUS > 4 + CMI0(4), CMI0(5), CMI0(6), CMI0(7), +#endif +#if NR_CPUS > 8 + CMI0(8), CMI0(9), CMI0(10), CMI0(11), + CMI0(12), CMI0(13), CMI0(14), CMI0(15), +#endif +#if NR_CPUS > 16 + CMI0(16), CMI0(17), CMI0(18), CMI0(19), + CMI0(20), CMI0(21), CMI0(22), CMI0(23), + CMI0(24), CMI0(25), CMI0(26), CMI0(27), + CMI0(28), CMI0(29), CMI0(30), CMI0(31), +#endif +#if NR_CPUS > 32 +#if BITS_PER_LONG == 32 + CMI(32, 0), CMI(33, 0), CMI(34, 0), CMI(35, 0), + CMI(36, 0), CMI(37, 0), CMI(38, 0), CMI(39, 0), + CMI(40, 0), CMI(41, 0), CMI(42, 0), CMI(43, 0), + CMI(44, 0), CMI(45, 0), CMI(46, 0), CMI(47, 0), + CMI(48, 0), CMI(49, 0), CMI(50, 0), CMI(51, 0), + CMI(52, 0), CMI(53, 0), CMI(54, 0), CMI(55, 0), + CMI(56, 0), CMI(57, 0), CMI(58, 0), CMI(59, 0), + CMI(60, 0), CMI(61, 0), CMI(62, 0), CMI(63, 0), +#else + CMI0(32), CMI0(33), CMI0(34), CMI0(35), + CMI0(36), CMI0(37), CMI0(38), CMI0(39), + CMI0(40), CMI0(41), CMI0(42), CMI0(43), + CMI0(44), CMI0(45), CMI0(46), CMI0(47), + CMI0(48), CMI0(49), CMI0(50), CMI0(51), + CMI0(52), CMI0(53), CMI0(54), CMI0(55), + CMI0(56), CMI0(57), CMI0(58), CMI0(59), + CMI0(60), CMI0(61), CMI0(62), CMI0(63), +#endif /* BITS_PER_LONG == 64 */ +#endif +#if NR_CPUS > 64 + CMI64(64, Z64), +#endif +#if NR_CPUS > 128 + CMI64(128, Z64, Z64), CMI64(192, Z64, Z64, Z64), +#endif +#if NR_CPUS > 256 + CMI256(256, Z256), +#endif +#if NR_CPUS > 512 + CMI256(512, Z256, Z256), CMI256(768, Z256, Z256, Z256), +#endif +#if NR_CPUS > 1024 + CMI1024(1024, Z1024), +#endif +#if NR_CPUS > 2048 + CMI1024(2048, Z1024, Z1024), CMI1024(3072, Z1024, Z1024, Z1024), +#endif +#if NR_CPUS > 4096 +#error NR_CPUS too big. Fix initializers or set CONFIG_HAVE_CPUMASK_OF_CPU_MAP +#endif +}; + +const cpumask_t *cpumask_of_cpu_map = cpumask_map; +#endif /* !CONFIG_HAVE_CPUMASK_OF_CPU_MAP */ -- cgit From fdd2a7e2dac56a3384068802be46b822f2aed703 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 26 Jul 2008 13:25:25 -0300 Subject: V4L/DVB (8500a): videotext.h: whitespace cleanup Signed-off-by: Mauro Carvalho Chehab --- include/linux/videotext.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/videotext.h b/include/linux/videotext.h index 018f92047ff8..3e68c8d1c7f7 100644 --- a/include/linux/videotext.h +++ b/include/linux/videotext.h @@ -45,10 +45,10 @@ #define VTXIOCCLRCACHE_OLD 0x710b /* clear cache on VTX-interface (if avail.) */ #define VTXIOCSETVIRT_OLD 0x710c /* turn on virtual mode (this disables TV-display) */ -/* +/* * Definitions for VTXIOCGETINFO */ - + #define SAA5243 0 #define SAA5246 1 #define SAA5249 2 @@ -57,10 +57,10 @@ typedef struct { int version_major, version_minor; /* version of driver; if version_major changes, driver */ - /* is not backward compatible!!! CHECK THIS!!! */ + /* is not backward compatible!!! CHECK THIS!!! */ int numpages; /* number of page-buffers of vtx-chipset */ int cct_type; /* type of vtx-chipset (SAA5243, SAA5246, SAA5248 or - * SAA5249) */ + * SAA5249) */ } vtx_info_t; @@ -81,7 +81,7 @@ vtx_info_t; #define PGMASK_HOUR (HR_TEN | HR_UNIT) #define PGMASK_MINUTE (MIN_TEN | MIN_UNIT) -typedef struct +typedef struct { int page; /* number of requested page (hexadecimal) */ int hour; /* requested hour (hexadecimal) */ @@ -98,11 +98,11 @@ vtx_pagereq_t; /* * Definitions for VTXIOC{GETSTAT,PUTSTAT} */ - + #define VTX_PAGESIZE (40 * 24) #define VTX_VIRTUALSIZE (40 * 49) -typedef struct +typedef struct { int pagenum; /* number of page (hexadecimal) */ int hour; /* hour (hexadecimal) */ @@ -121,5 +121,5 @@ typedef struct unsigned hamming : 1; /* hamming-error occurred */ } vtx_pageinfo_t; - + #endif /* _VTX_H */ -- cgit From 16d69265b930f7e2fa9eea381715696f780718f4 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 25 Jul 2008 19:44:36 -0700 Subject: uninline arch_pick_mmap_layout() Fix this, on avr32: include/linux/utsname.h:35, from init/main.c:20: include/linux/sched.h: In function 'arch_pick_mmap_layout': include/linux/sched.h:2149: error: implicit declaration of function 'PAGE_ALIGN' Reported-by: Adrian Bunk Cc: Haavard Skinnemoen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 9 --------- mm/util.c | 10 ++++++++++ 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 42036ffe6b00..3260a5c42b91 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2139,16 +2139,7 @@ static inline void set_task_cpu(struct task_struct *p, unsigned int cpu) #endif /* CONFIG_SMP */ -#ifdef HAVE_ARCH_PICK_MMAP_LAYOUT extern void arch_pick_mmap_layout(struct mm_struct *mm); -#else -static inline void arch_pick_mmap_layout(struct mm_struct *mm) -{ - mm->mmap_base = TASK_UNMAPPED_BASE; - mm->get_unmapped_area = arch_get_unmapped_area; - mm->unmap_area = arch_unmap_area; -} -#endif #ifdef CONFIG_TRACING extern void diff --git a/mm/util.c b/mm/util.c index 8f18683825bc..0efd83097ecf 100644 --- a/mm/util.c +++ b/mm/util.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -136,3 +137,12 @@ char *strndup_user(const char __user *s, long n) return p; } EXPORT_SYMBOL(strndup_user); + +#ifndef HAVE_ARCH_PICK_MMAP_LAYOUT +void arch_pick_mmap_layout(struct mm_struct *mm) +{ + mm->mmap_base = TASK_UNMAPPED_BASE; + mm->get_unmapped_area = arch_get_unmapped_area; + mm->unmap_area = arch_unmap_area; +} +#endif -- cgit From 8d8bb39b9eba32dd70e87fd5ad5c5dd4ba118e06 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 25 Jul 2008 19:44:49 -0700 Subject: dma-mapping: add the device argument to dma_mapping_error() Add per-device dma_mapping_ops support for CONFIG_X86_64 as POWER architecture does: This enables us to cleanly fix the Calgary IOMMU issue that some devices are not behind the IOMMU (http://lkml.org/lkml/2008/5/8/423). I think that per-device dma_mapping_ops support would be also helpful for KVM people to support PCI passthrough but Andi thinks that this makes it difficult to support the PCI passthrough (see the above thread). So I CC'ed this to KVM camp. Comments are appreciated. A pointer to dma_mapping_ops to struct dev_archdata is added. If the pointer is non NULL, DMA operations in asm/dma-mapping.h use it. If it's NULL, the system-wide dma_ops pointer is used as before. If it's useful for KVM people, I plan to implement a mechanism to register a hook called when a new pci (or dma capable) device is created (it works with hot plugging). It enables IOMMUs to set up an appropriate dma_mapping_ops per device. The major obstacle is that dma_mapping_error doesn't take a pointer to the device unlike other DMA operations. So x86 can't have dma_mapping_ops per device. Note all the POWER IOMMUs use the same dma_mapping_error function so this is not a problem for POWER but x86 IOMMUs use different dma_mapping_error functions. The first patch adds the device argument to dma_mapping_error. The patch is trivial but large since it touches lots of drivers and dma-mapping.h in all the architecture. This patch: dma_mapping_error() doesn't take a pointer to the device unlike other DMA operations. So we can't have dma_mapping_ops per device. Note that POWER already has dma_mapping_ops per device but all the POWER IOMMUs use the same dma_mapping_error function. x86 IOMMUs use device argument. [akpm@linux-foundation.org: fix sge] [akpm@linux-foundation.org: fix svc_rdma] [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: fix bnx2x] [akpm@linux-foundation.org: fix s2io] [akpm@linux-foundation.org: fix pasemi_mac] [akpm@linux-foundation.org: fix sdhci] [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: fix sparc] [akpm@linux-foundation.org: fix ibmvscsi] Signed-off-by: FUJITA Tomonori Cc: Muli Ben-Yehuda Cc: Andi Kleen Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Avi Kivity Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/DMA-API.txt | 4 +- arch/arm/common/dmabounce.c | 2 +- arch/ia64/hp/common/hwsw_iommu.c | 5 +- arch/ia64/hp/common/sba_iommu.c | 2 +- arch/ia64/sn/pci/pci_dma.c | 2 +- arch/mips/mm/dma-default.c | 2 +- arch/powerpc/platforms/cell/celleb_scc_pciex.c | 2 +- arch/powerpc/platforms/cell/spider-pci.c | 2 +- arch/powerpc/platforms/iseries/mf.c | 2 +- arch/x86/kernel/pci-calgary_64.c | 2 +- arch/x86/kernel/pci-dma.c | 27 ++++--- arch/x86/kernel/pci-gart_64.c | 3 +- arch/x86/kernel/pci-nommu.c | 14 +--- arch/x86/kernel/pci-swiotlb_64.c | 2 +- drivers/firewire/fw-iso.c | 2 +- drivers/firewire/fw-ohci.c | 2 +- drivers/firewire/fw-sbp2.c | 8 +-- drivers/infiniband/hw/ipath/ipath_sdma.c | 2 +- drivers/infiniband/hw/ipath/ipath_user_sdma.c | 6 +- drivers/infiniband/hw/mthca/mthca_eq.c | 2 +- drivers/media/dvb/pluto2/pluto2.c | 2 +- drivers/mmc/host/sdhci.c | 4 +- drivers/net/arm/ep93xx_eth.c | 4 +- drivers/net/bnx2x_main.c | 4 +- drivers/net/cxgb3/sge.c | 2 +- drivers/net/e100.c | 2 +- drivers/net/e1000e/ethtool.c | 4 +- drivers/net/e1000e/netdev.c | 11 +-- drivers/net/ibmveth.c | 38 +++++----- drivers/net/iseries_veth.c | 4 +- drivers/net/mlx4/eq.c | 2 +- drivers/net/pasemi_mac.c | 6 +- drivers/net/qla3xxx.c | 12 ++-- drivers/net/s2io.c | 48 +++++++------ drivers/net/sfc/rx.c | 4 +- drivers/net/sfc/tx.c | 7 +- drivers/net/spider_net.c | 4 +- drivers/net/tc35815.c | 4 +- drivers/net/wireless/ath5k/base.c | 4 +- drivers/scsi/ibmvscsi/ibmvfc.c | 4 +- drivers/scsi/ibmvscsi/ibmvscsi.c | 4 +- drivers/scsi/ibmvscsi/ibmvstgt.c | 2 +- drivers/scsi/ibmvscsi/rpa_vscsi.c | 2 +- drivers/spi/atmel_spi.c | 4 +- drivers/spi/au1550_spi.c | 6 +- drivers/spi/omap2_mcspi.c | 4 +- drivers/spi/pxa2xx_spi.c | 4 +- drivers/spi/spi_imx.c | 6 +- include/asm-alpha/dma-mapping.h | 6 +- include/asm-alpha/pci.h | 2 +- include/asm-arm/dma-mapping.h | 2 +- include/asm-avr32/dma-mapping.h | 2 +- include/asm-cris/dma-mapping.h | 2 +- include/asm-frv/dma-mapping.h | 2 +- include/asm-generic/dma-mapping-broken.h | 2 +- include/asm-generic/dma-mapping.h | 4 +- include/asm-generic/pci-dma-compat.h | 4 +- include/asm-ia64/machvec.h | 2 +- include/asm-m68k/dma-mapping.h | 2 +- include/asm-mips/dma-mapping.h | 2 +- include/asm-mn10300/dma-mapping.h | 2 +- include/asm-parisc/dma-mapping.h | 2 +- include/asm-powerpc/dma-mapping.h | 2 +- include/asm-sh/dma-mapping.h | 2 +- include/asm-sparc/dma-mapping_64.h | 2 +- include/asm-sparc/pci_32.h | 3 +- include/asm-sparc/pci_64.h | 5 +- include/asm-x86/device.h | 3 + include/asm-x86/dma-mapping.h | 99 ++++++++++++++++++-------- include/asm-x86/swiotlb.h | 2 +- include/asm-xtensa/dma-mapping.h | 2 +- include/linux/i2o.h | 2 +- include/linux/ssb/ssb.h | 4 +- include/rdma/ib_verbs.h | 2 +- lib/swiotlb.c | 4 +- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 3 +- 76 files changed, 256 insertions(+), 210 deletions(-) (limited to 'include/linux') diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index 80d150458c80..d8b63d164e41 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt @@ -298,10 +298,10 @@ recommended that you never use these unless you really know what the cache width is. int -dma_mapping_error(dma_addr_t dma_addr) +dma_mapping_error(struct device *dev, dma_addr_t dma_addr) int -pci_dma_mapping_error(dma_addr_t dma_addr) +pci_dma_mapping_error(struct pci_dev *hwdev, dma_addr_t dma_addr) In some circumstances dma_map_single and dma_map_page will fail to create a mapping. A driver can check for these errors by testing the returned diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c index dd2947342604..69130f365904 100644 --- a/arch/arm/common/dmabounce.c +++ b/arch/arm/common/dmabounce.c @@ -280,7 +280,7 @@ unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, /* * Trying to unmap an invalid mapping */ - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(dev, dma_addr)) { dev_err(dev, "Trying to unmap invalid mapping\n"); return; } diff --git a/arch/ia64/hp/common/hwsw_iommu.c b/arch/ia64/hp/common/hwsw_iommu.c index 1c44ec2a1d58..88b6e6f3fd88 100644 --- a/arch/ia64/hp/common/hwsw_iommu.c +++ b/arch/ia64/hp/common/hwsw_iommu.c @@ -186,9 +186,10 @@ hwsw_dma_supported (struct device *dev, u64 mask) } int -hwsw_dma_mapping_error (dma_addr_t dma_addr) +hwsw_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { - return hwiommu_dma_mapping_error (dma_addr) || swiotlb_dma_mapping_error(dma_addr); + return hwiommu_dma_mapping_error(dev, dma_addr) || + swiotlb_dma_mapping_error(dev, dma_addr); } EXPORT_SYMBOL(hwsw_dma_mapping_error); diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c index 34421aed1e2a..4956be40d7b5 100644 --- a/arch/ia64/hp/common/sba_iommu.c +++ b/arch/ia64/hp/common/sba_iommu.c @@ -2147,7 +2147,7 @@ sba_dma_supported (struct device *dev, u64 mask) } int -sba_dma_mapping_error (dma_addr_t dma_addr) +sba_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/arch/ia64/sn/pci/pci_dma.c b/arch/ia64/sn/pci/pci_dma.c index 52175af299a0..53ebb6484495 100644 --- a/arch/ia64/sn/pci/pci_dma.c +++ b/arch/ia64/sn/pci/pci_dma.c @@ -350,7 +350,7 @@ void sn_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, } EXPORT_SYMBOL(sn_dma_sync_sg_for_device); -int sn_dma_mapping_error(dma_addr_t dma_addr) +int sn_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c index ae39dd88b9aa..891312f8e5a6 100644 --- a/arch/mips/mm/dma-default.c +++ b/arch/mips/mm/dma-default.c @@ -348,7 +348,7 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nele EXPORT_SYMBOL(dma_sync_sg_for_device); -int dma_mapping_error(dma_addr_t dma_addr) +int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c index 0e04f8fb152a..3e7e0f1568ef 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_pciex.c +++ b/arch/powerpc/platforms/cell/celleb_scc_pciex.c @@ -281,7 +281,7 @@ static int __init scc_pciex_iowa_init(struct iowa_bus *bus, void *data) dummy_page_da = dma_map_single(bus->phb->parent, dummy_page_va, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dummy_page_da)) { + if (dma_mapping_error(bus->phb->parent, dummy_page_da)) { pr_err("PCIEX:Map dummy page failed.\n"); kfree(dummy_page_va); return -1; diff --git a/arch/powerpc/platforms/cell/spider-pci.c b/arch/powerpc/platforms/cell/spider-pci.c index 418b605ac35a..5122ec145271 100644 --- a/arch/powerpc/platforms/cell/spider-pci.c +++ b/arch/powerpc/platforms/cell/spider-pci.c @@ -111,7 +111,7 @@ static int __init spiderpci_pci_setup_chip(struct pci_controller *phb, dummy_page_da = dma_map_single(phb->parent, dummy_page_va, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dummy_page_da)) { + if (dma_mapping_error(phb->parent, dummy_page_da)) { pr_err("SPIDER-IOWA:Map dummy page filed.\n"); kfree(dummy_page_va); return -1; diff --git a/arch/powerpc/platforms/iseries/mf.c b/arch/powerpc/platforms/iseries/mf.c index 1dc7295746da..731d7b157749 100644 --- a/arch/powerpc/platforms/iseries/mf.c +++ b/arch/powerpc/platforms/iseries/mf.c @@ -871,7 +871,7 @@ static int proc_mf_dump_cmdline(char *page, char **start, off_t off, count = 256 - off; dma_addr = iseries_hv_map(page, off + count, DMA_FROM_DEVICE); - if (dma_mapping_error(dma_addr)) + if (dma_mapping_error(NULL, dma_addr)) return -ENOMEM; memset(page, 0, off + count); memset(&vsp_cmd, 0, sizeof(vsp_cmd)); diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c index 19e7fc7c2c4f..1eb86be93d7a 100644 --- a/arch/x86/kernel/pci-calgary_64.c +++ b/arch/x86/kernel/pci-calgary_64.c @@ -544,7 +544,7 @@ error: return ret; } -static const struct dma_mapping_ops calgary_dma_ops = { +static struct dma_mapping_ops calgary_dma_ops = { .alloc_coherent = calgary_alloc_coherent, .map_single = calgary_map_single, .unmap_single = calgary_unmap_single, diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index cbecb05551bb..37544123896d 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c @@ -11,7 +11,7 @@ static int forbid_dac __read_mostly; -const struct dma_mapping_ops *dma_ops; +struct dma_mapping_ops *dma_ops; EXPORT_SYMBOL(dma_ops); static int iommu_sac_force __read_mostly; @@ -312,6 +312,8 @@ static int dma_release_coherent(struct device *dev, int order, void *vaddr) int dma_supported(struct device *dev, u64 mask) { + struct dma_mapping_ops *ops = get_dma_ops(dev); + #ifdef CONFIG_PCI if (mask > 0xffffffff && forbid_dac > 0) { dev_info(dev, "PCI: Disallowing DAC for device\n"); @@ -319,8 +321,8 @@ int dma_supported(struct device *dev, u64 mask) } #endif - if (dma_ops->dma_supported) - return dma_ops->dma_supported(dev, mask); + if (ops->dma_supported) + return ops->dma_supported(dev, mask); /* Copied from i386. Doesn't make much sense, because it will only work for pci_alloc_coherent. @@ -367,6 +369,7 @@ void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp) { + struct dma_mapping_ops *ops = get_dma_ops(dev); void *memory = NULL; struct page *page; unsigned long dma_mask = 0; @@ -435,8 +438,8 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, /* Let low level make its own zone decisions */ gfp &= ~(GFP_DMA32|GFP_DMA); - if (dma_ops->alloc_coherent) - return dma_ops->alloc_coherent(dev, size, + if (ops->alloc_coherent) + return ops->alloc_coherent(dev, size, dma_handle, gfp); return NULL; } @@ -448,14 +451,14 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, } } - if (dma_ops->alloc_coherent) { + if (ops->alloc_coherent) { free_pages((unsigned long)memory, get_order(size)); gfp &= ~(GFP_DMA|GFP_DMA32); - return dma_ops->alloc_coherent(dev, size, dma_handle, gfp); + return ops->alloc_coherent(dev, size, dma_handle, gfp); } - if (dma_ops->map_simple) { - *dma_handle = dma_ops->map_simple(dev, virt_to_phys(memory), + if (ops->map_simple) { + *dma_handle = ops->map_simple(dev, virt_to_phys(memory), size, PCI_DMA_BIDIRECTIONAL); if (*dma_handle != bad_dma_address) @@ -477,12 +480,14 @@ EXPORT_SYMBOL(dma_alloc_coherent); void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t bus) { + struct dma_mapping_ops *ops = get_dma_ops(dev); + int order = get_order(size); WARN_ON(irqs_disabled()); /* for portability */ if (dma_release_coherent(dev, order, vaddr)) return; - if (dma_ops->unmap_single) - dma_ops->unmap_single(dev, bus, size, 0); + if (ops->unmap_single) + ops->unmap_single(dev, bus, size, 0); free_pages((unsigned long)vaddr, order); } EXPORT_SYMBOL(dma_free_coherent); diff --git a/arch/x86/kernel/pci-gart_64.c b/arch/x86/kernel/pci-gart_64.c index df5f142657d2..744126e64950 100644 --- a/arch/x86/kernel/pci-gart_64.c +++ b/arch/x86/kernel/pci-gart_64.c @@ -692,8 +692,7 @@ static __init int init_k8_gatt(struct agp_kern_info *info) extern int agp_amd64_init(void); -static const struct dma_mapping_ops gart_dma_ops = { - .mapping_error = NULL, +static struct dma_mapping_ops gart_dma_ops = { .map_single = gart_map_single, .map_simple = gart_map_simple, .unmap_single = gart_unmap_single, diff --git a/arch/x86/kernel/pci-nommu.c b/arch/x86/kernel/pci-nommu.c index 792b9179eff3..3f91f71cdc3e 100644 --- a/arch/x86/kernel/pci-nommu.c +++ b/arch/x86/kernel/pci-nommu.c @@ -72,21 +72,9 @@ static int nommu_map_sg(struct device *hwdev, struct scatterlist *sg, return nents; } -/* Make sure we keep the same behaviour */ -static int nommu_mapping_error(dma_addr_t dma_addr) -{ -#ifdef CONFIG_X86_32 - return 0; -#else - return (dma_addr == bad_dma_address); -#endif -} - - -const struct dma_mapping_ops nommu_dma_ops = { +struct dma_mapping_ops nommu_dma_ops = { .map_single = nommu_map_single, .map_sg = nommu_map_sg, - .mapping_error = nommu_mapping_error, .is_phys = 1, }; diff --git a/arch/x86/kernel/pci-swiotlb_64.c b/arch/x86/kernel/pci-swiotlb_64.c index 20df839b9c20..c4ce0332759e 100644 --- a/arch/x86/kernel/pci-swiotlb_64.c +++ b/arch/x86/kernel/pci-swiotlb_64.c @@ -18,7 +18,7 @@ swiotlb_map_single_phys(struct device *hwdev, phys_addr_t paddr, size_t size, return swiotlb_map_single(hwdev, phys_to_virt(paddr), size, direction); } -const struct dma_mapping_ops swiotlb_dma_ops = { +struct dma_mapping_ops swiotlb_dma_ops = { .mapping_error = swiotlb_dma_mapping_error, .alloc_coherent = swiotlb_alloc_coherent, .free_coherent = swiotlb_free_coherent, diff --git a/drivers/firewire/fw-iso.c b/drivers/firewire/fw-iso.c index bcbe794a3ea5..e14c03dc0065 100644 --- a/drivers/firewire/fw-iso.c +++ b/drivers/firewire/fw-iso.c @@ -50,7 +50,7 @@ fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, address = dma_map_page(card->device, buffer->pages[i], 0, PAGE_SIZE, direction); - if (dma_mapping_error(address)) { + if (dma_mapping_error(card->device, address)) { __free_page(buffer->pages[i]); goto out_pages; } diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 333b12544dd1..566672e0bcff 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -953,7 +953,7 @@ at_context_queue_packet(struct context *ctx, struct fw_packet *packet) payload_bus = dma_map_single(ohci->card.device, packet->payload, packet->payload_length, DMA_TO_DEVICE); - if (dma_mapping_error(payload_bus)) { + if (dma_mapping_error(ohci->card.device, payload_bus)) { packet->ack = RCODE_SEND_ERROR; return -1; } diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 53fc5a641e6d..aaff50ebba1d 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -543,7 +543,7 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, orb->response_bus = dma_map_single(device->card->device, &orb->response, sizeof(orb->response), DMA_FROM_DEVICE); - if (dma_mapping_error(orb->response_bus)) + if (dma_mapping_error(device->card->device, orb->response_bus)) goto fail_mapping_response; orb->request.response.high = 0; @@ -577,7 +577,7 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, orb->base.request_bus = dma_map_single(device->card->device, &orb->request, sizeof(orb->request), DMA_TO_DEVICE); - if (dma_mapping_error(orb->base.request_bus)) + if (dma_mapping_error(device->card->device, orb->base.request_bus)) goto fail_mapping_request; sbp2_send_orb(&orb->base, lu, node_id, generation, @@ -1424,7 +1424,7 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, orb->page_table_bus = dma_map_single(device->card->device, orb->page_table, sizeof(orb->page_table), DMA_TO_DEVICE); - if (dma_mapping_error(orb->page_table_bus)) + if (dma_mapping_error(device->card->device, orb->page_table_bus)) goto fail_page_table; /* @@ -1509,7 +1509,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) orb->base.request_bus = dma_map_single(device->card->device, &orb->request, sizeof(orb->request), DMA_TO_DEVICE); - if (dma_mapping_error(orb->base.request_bus)) + if (dma_mapping_error(device->card->device, orb->base.request_bus)) goto out; sbp2_send_orb(&orb->base, lu, lu->tgt->node_id, lu->generation, diff --git a/drivers/infiniband/hw/ipath/ipath_sdma.c b/drivers/infiniband/hw/ipath/ipath_sdma.c index eaba03273e4f..284c9bca517e 100644 --- a/drivers/infiniband/hw/ipath/ipath_sdma.c +++ b/drivers/infiniband/hw/ipath/ipath_sdma.c @@ -698,7 +698,7 @@ retry: addr = dma_map_single(&dd->pcidev->dev, tx->txreq.map_addr, tx->map_len, DMA_TO_DEVICE); - if (dma_mapping_error(addr)) { + if (dma_mapping_error(&dd->pcidev->dev, addr)) { ret = -EIO; goto unlock; } diff --git a/drivers/infiniband/hw/ipath/ipath_user_sdma.c b/drivers/infiniband/hw/ipath/ipath_user_sdma.c index 86e016916cd1..82d9a0b5ca2f 100644 --- a/drivers/infiniband/hw/ipath/ipath_user_sdma.c +++ b/drivers/infiniband/hw/ipath/ipath_user_sdma.c @@ -206,7 +206,7 @@ static int ipath_user_sdma_coalesce(const struct ipath_devdata *dd, dma_addr = dma_map_page(&dd->pcidev->dev, page, 0, len, DMA_TO_DEVICE); - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) { ret = -ENOMEM; goto free_unmap; } @@ -301,7 +301,7 @@ static int ipath_user_sdma_pin_pages(const struct ipath_devdata *dd, pages[j], 0, flen, DMA_TO_DEVICE); unsigned long fofs = addr & ~PAGE_MASK; - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) { ret = -ENOMEM; goto done; } @@ -508,7 +508,7 @@ static int ipath_user_sdma_queue_pkts(const struct ipath_devdata *dd, if (page) { dma_addr = dma_map_page(&dd->pcidev->dev, page, 0, len, DMA_TO_DEVICE); - if (dma_mapping_error(dma_addr)) { + if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) { ret = -ENOMEM; goto free_pbc; } diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c index 4e36aa7cb3d2..cc6858f0b65b 100644 --- a/drivers/infiniband/hw/mthca/mthca_eq.c +++ b/drivers/infiniband/hw/mthca/mthca_eq.c @@ -780,7 +780,7 @@ int mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt) return -ENOMEM; dev->eq_table.icm_dma = pci_map_page(dev->pdev, dev->eq_table.icm_page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev->eq_table.icm_dma)) { + if (pci_dma_mapping_error(dev->pdev, dev->eq_table.icm_dma)) { __free_page(dev->eq_table.icm_page); return -ENOMEM; } diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c index 1360403b88b6..a9653c63f4db 100644 --- a/drivers/media/dvb/pluto2/pluto2.c +++ b/drivers/media/dvb/pluto2/pluto2.c @@ -242,7 +242,7 @@ static int __devinit pluto_dma_map(struct pluto *pluto) pluto->dma_addr = pci_map_single(pluto->pdev, pluto->dma_buf, TS_DMA_BYTES, PCI_DMA_FROMDEVICE); - return pci_dma_mapping_error(pluto->dma_addr); + return pci_dma_mapping_error(pluto->pdev, pluto->dma_addr); } static void pluto_dma_unmap(struct pluto *pluto) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c3a5db72ddd7..5f95e10229b5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -337,7 +337,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, host->align_addr = dma_map_single(mmc_dev(host->mmc), host->align_buffer, 128 * 4, direction); - if (dma_mapping_error(host->align_addr)) + if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) goto fail; BUG_ON(host->align_addr & 0x3); @@ -439,7 +439,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, host->adma_addr = dma_map_single(mmc_dev(host->mmc), host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE); - if (dma_mapping_error(host->align_addr)) + if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) goto unmap_entries; BUG_ON(host->adma_addr & 0x3); diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c index 7a14980f3472..18d3eeb7eab2 100644 --- a/drivers/net/arm/ep93xx_eth.c +++ b/drivers/net/arm/ep93xx_eth.c @@ -482,7 +482,7 @@ static int ep93xx_alloc_buffers(struct ep93xx_priv *ep) goto err; d = dma_map_single(NULL, page, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(d)) { + if (dma_mapping_error(NULL, d)) { free_page((unsigned long)page); goto err; } @@ -505,7 +505,7 @@ static int ep93xx_alloc_buffers(struct ep93xx_priv *ep) goto err; d = dma_map_single(NULL, page, PAGE_SIZE, DMA_TO_DEVICE); - if (dma_mapping_error(d)) { + if (dma_mapping_error(NULL, d)) { free_page((unsigned long)page); goto err; } diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 0263bef9cc6d..c7cc760a1777 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -1020,7 +1020,7 @@ static inline int bnx2x_alloc_rx_sge(struct bnx2x *bp, mapping = pci_map_page(bp->pdev, page, 0, BCM_PAGE_SIZE*PAGES_PER_SGE, PCI_DMA_FROMDEVICE); - if (unlikely(dma_mapping_error(mapping))) { + if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) { __free_pages(page, PAGES_PER_SGE_SHIFT); return -ENOMEM; } @@ -1048,7 +1048,7 @@ static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp, mapping = pci_map_single(bp->pdev, skb->data, bp->rx_buf_use_size, PCI_DMA_FROMDEVICE); - if (unlikely(dma_mapping_error(mapping))) { + if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) { dev_kfree_skb(skb); return -ENOMEM; } diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index a96331c875e6..1b0861d73ab7 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -386,7 +386,7 @@ static inline int add_one_rx_buf(void *va, unsigned int len, dma_addr_t mapping; mapping = pci_map_single(pdev, va, len, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(mapping))) + if (unlikely(pci_dma_mapping_error(pdev, mapping))) return -ENOMEM; pci_unmap_addr_set(sd, dma_addr, mapping); diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 1037b1332312..19d32a227be1 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -1790,7 +1790,7 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data, RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(rx->dma_addr)) { + if (pci_dma_mapping_error(nic->pdev, rx->dma_addr)) { dev_kfree_skb_any(rx->skb); rx->skb = NULL; rx->dma_addr = 0; diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c index a14561f40db0..9350564065e7 100644 --- a/drivers/net/e1000e/ethtool.c +++ b/drivers/net/e1000e/ethtool.c @@ -1090,7 +1090,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter) tx_ring->buffer_info[i].dma = pci_map_single(pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(tx_ring->buffer_info[i].dma)) { + if (pci_dma_mapping_error(pdev, tx_ring->buffer_info[i].dma)) { ret_val = 4; goto err_nomem; } @@ -1153,7 +1153,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter) rx_ring->buffer_info[i].dma = pci_map_single(pdev, skb->data, 2048, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rx_ring->buffer_info[i].dma)) { + if (pci_dma_mapping_error(pdev, rx_ring->buffer_info[i].dma)) { ret_val = 8; goto err_nomem; } diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 9c0f56b3c518..d13677899767 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -195,7 +195,7 @@ map_skb: buffer_info->dma = pci_map_single(pdev, skb->data, adapter->rx_buffer_len, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(pdev, buffer_info->dma)) { dev_err(&pdev->dev, "RX DMA map failed\n"); adapter->rx_dma_failed++; break; @@ -265,7 +265,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter, ps_page->page, 0, PAGE_SIZE, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(ps_page->dma)) { + if (pci_dma_mapping_error(pdev, ps_page->dma)) { dev_err(&adapter->pdev->dev, "RX DMA page map failed\n"); adapter->rx_dma_failed++; @@ -300,7 +300,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter, buffer_info->dma = pci_map_single(pdev, skb->data, adapter->rx_ps_bsize0, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(pdev, buffer_info->dma)) { dev_err(&pdev->dev, "RX DMA map failed\n"); adapter->rx_dma_failed++; /* cleanup skb */ @@ -3344,7 +3344,7 @@ static int e1000_tx_map(struct e1000_adapter *adapter, skb->data + offset, size, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(adapter->pdev, buffer_info->dma)) { dev_err(&adapter->pdev->dev, "TX DMA map failed\n"); adapter->tx_dma_failed++; return -1; @@ -3382,7 +3382,8 @@ static int e1000_tx_map(struct e1000_adapter *adapter, offset, size, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(buffer_info->dma)) { + if (pci_dma_mapping_error(adapter->pdev, + buffer_info->dma)) { dev_err(&adapter->pdev->dev, "TX DMA page map failed\n"); adapter->tx_dma_failed++; diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index e5a6e2e84540..91ec9fdc7184 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -260,7 +260,7 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc dma_addr = dma_map_single(&adapter->vdev->dev, skb->data, pool->buff_size, DMA_FROM_DEVICE); - if (dma_mapping_error(dma_addr)) + if (dma_mapping_error((&adapter->vdev->dev, dma_addr)) goto failure; pool->free_map[free_index] = IBM_VETH_INVALID_MAP; @@ -294,7 +294,7 @@ failure: pool->consumer_index = pool->size - 1; else pool->consumer_index--; - if (!dma_mapping_error(dma_addr)) + if (!dma_mapping_error((&adapter->vdev->dev, dma_addr)) dma_unmap_single(&adapter->vdev->dev, pool->dma_addr[index], pool->buff_size, DMA_FROM_DEVICE); @@ -448,11 +448,11 @@ static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter) static void ibmveth_cleanup(struct ibmveth_adapter *adapter) { int i; + struct device *dev = &adapter->vdev->dev; if(adapter->buffer_list_addr != NULL) { - if(!dma_mapping_error(adapter->buffer_list_dma)) { - dma_unmap_single(&adapter->vdev->dev, - adapter->buffer_list_dma, 4096, + if (!dma_mapping_error(dev, adapter->buffer_list_dma)) { + dma_unmap_single(dev, adapter->buffer_list_dma, 4096, DMA_BIDIRECTIONAL); adapter->buffer_list_dma = DMA_ERROR_CODE; } @@ -461,9 +461,8 @@ static void ibmveth_cleanup(struct ibmveth_adapter *adapter) } if(adapter->filter_list_addr != NULL) { - if(!dma_mapping_error(adapter->filter_list_dma)) { - dma_unmap_single(&adapter->vdev->dev, - adapter->filter_list_dma, 4096, + if (!dma_mapping_error(dev, adapter->filter_list_dma)) { + dma_unmap_single(dev, adapter->filter_list_dma, 4096, DMA_BIDIRECTIONAL); adapter->filter_list_dma = DMA_ERROR_CODE; } @@ -472,8 +471,8 @@ static void ibmveth_cleanup(struct ibmveth_adapter *adapter) } if(adapter->rx_queue.queue_addr != NULL) { - if(!dma_mapping_error(adapter->rx_queue.queue_dma)) { - dma_unmap_single(&adapter->vdev->dev, + if (!dma_mapping_error(dev, adapter->rx_queue.queue_dma)) { + dma_unmap_single(dev, adapter->rx_queue.queue_dma, adapter->rx_queue.queue_len, DMA_BIDIRECTIONAL); @@ -535,6 +534,7 @@ static int ibmveth_open(struct net_device *netdev) int rc; union ibmveth_buf_desc rxq_desc; int i; + struct device *dev; ibmveth_debug_printk("open starting\n"); @@ -563,17 +563,19 @@ static int ibmveth_open(struct net_device *netdev) return -ENOMEM; } - adapter->buffer_list_dma = dma_map_single(&adapter->vdev->dev, + dev = &adapter->vdev->dev; + + adapter->buffer_list_dma = dma_map_single(dev, adapter->buffer_list_addr, 4096, DMA_BIDIRECTIONAL); - adapter->filter_list_dma = dma_map_single(&adapter->vdev->dev, + adapter->filter_list_dma = dma_map_single(dev, adapter->filter_list_addr, 4096, DMA_BIDIRECTIONAL); - adapter->rx_queue.queue_dma = dma_map_single(&adapter->vdev->dev, + adapter->rx_queue.queue_dma = dma_map_single(dev, adapter->rx_queue.queue_addr, adapter->rx_queue.queue_len, DMA_BIDIRECTIONAL); - if((dma_mapping_error(adapter->buffer_list_dma) ) || - (dma_mapping_error(adapter->filter_list_dma)) || - (dma_mapping_error(adapter->rx_queue.queue_dma))) { + if ((dma_mapping_error(dev, adapter->buffer_list_dma)) || + (dma_mapping_error(dev, adapter->filter_list_dma)) || + (dma_mapping_error(dev, adapter->rx_queue.queue_dma))) { ibmveth_error_printk("unable to map filter or buffer list pages\n"); ibmveth_cleanup(adapter); napi_disable(&adapter->napi); @@ -645,7 +647,7 @@ static int ibmveth_open(struct net_device *netdev) adapter->bounce_buffer_dma = dma_map_single(&adapter->vdev->dev, adapter->bounce_buffer, netdev->mtu + IBMVETH_BUFF_OH, DMA_BIDIRECTIONAL); - if (dma_mapping_error(adapter->bounce_buffer_dma)) { + if (dma_mapping_error(dev, adapter->bounce_buffer_dma)) { ibmveth_error_printk("unable to map bounce buffer\n"); ibmveth_cleanup(adapter); napi_disable(&adapter->napi); @@ -922,7 +924,7 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) buf[1] = 0; } - if (dma_mapping_error(data_dma_addr)) { + if (dma_mapping_error((&adapter->vdev->dev, data_dma_addr)) { if (!firmware_has_feature(FW_FEATURE_CMO)) ibmveth_error_printk("tx: unable to map xmit buffer\n"); skb_copy_from_linear_data(skb, adapter->bounce_buffer, diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index b8d0639c1cdf..c46864d626b2 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -1128,7 +1128,7 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, msg->data.addr[0] = dma_map_single(port->dev, skb->data, skb->len, DMA_TO_DEVICE); - if (dma_mapping_error(msg->data.addr[0])) + if (dma_mapping_error(port->dev, msg->data.addr[0])) goto recycle_and_drop; msg->dev = port->dev; @@ -1226,7 +1226,7 @@ static void veth_recycle_msg(struct veth_lpar_connection *cnx, dma_address = msg->data.addr[0]; dma_length = msg->data.len[0]; - if (!dma_mapping_error(dma_address)) + if (!dma_mapping_error(msg->dev, dma_address)) dma_unmap_single(msg->dev, dma_address, dma_length, DMA_TO_DEVICE); diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c index ea3a09aaa844..7df928d3a3d8 100644 --- a/drivers/net/mlx4/eq.c +++ b/drivers/net/mlx4/eq.c @@ -526,7 +526,7 @@ int mlx4_map_eq_icm(struct mlx4_dev *dev, u64 icm_virt) return -ENOMEM; priv->eq_table.icm_dma = pci_map_page(dev->pdev, priv->eq_table.icm_page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(priv->eq_table.icm_dma)) { + if (pci_dma_mapping_error(dev->pdev, priv->eq_table.icm_dma)) { __free_page(priv->eq_table.icm_page); return -ENOMEM; } diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c index 993d87c9296f..edc0fd588985 100644 --- a/drivers/net/pasemi_mac.c +++ b/drivers/net/pasemi_mac.c @@ -650,7 +650,7 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev, mac->bufsz - LOCAL_SKB_ALIGN, PCI_DMA_FROMDEVICE); - if (unlikely(dma_mapping_error(dma))) { + if (unlikely(pci_dma_mapping_error(mac->dma_pdev, dma))) { dev_kfree_skb_irq(info->skb); break; } @@ -1519,7 +1519,7 @@ static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev) map[0] = pci_map_single(mac->dma_pdev, skb->data, skb_headlen(skb), PCI_DMA_TODEVICE); map_size[0] = skb_headlen(skb); - if (dma_mapping_error(map[0])) + if (pci_dma_mapping_error(mac->dma_pdev, map[0])) goto out_err_nolock; for (i = 0; i < nfrags; i++) { @@ -1529,7 +1529,7 @@ static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev) frag->page_offset, frag->size, PCI_DMA_TODEVICE); map_size[i+1] = frag->size; - if (dma_mapping_error(map[i+1])) { + if (pci_dma_mapping_error(mac->dma_pdev, map[i+1])) { nfrags = i; goto out_err_nolock; } diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index e7d48a352beb..e82b37bbd6c3 100644 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -328,7 +328,7 @@ static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev, qdev->lrg_buffer_len - QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); @@ -1919,7 +1919,7 @@ static int ql_populate_free_queue(struct ql3_adapter *qdev) QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); @@ -2454,7 +2454,7 @@ static int ql_send_map(struct ql3_adapter *qdev, */ map = pci_map_single(qdev->pdev, skb->data, len, PCI_DMA_TODEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); @@ -2487,7 +2487,7 @@ static int ql_send_map(struct ql3_adapter *qdev, sizeof(struct oal), PCI_DMA_TODEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping outbound address list with error: %d\n", @@ -2514,7 +2514,7 @@ static int ql_send_map(struct ql3_adapter *qdev, frag->page_offset, frag->size, PCI_DMA_TODEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping frags failed with error: %d\n", qdev->ndev->name, err); @@ -2916,7 +2916,7 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev) QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); - err = pci_dma_mapping_error(map); + err = pci_dma_mapping_error(qdev->pdev, map); if(err) { printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", qdev->ndev->name, err); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 9dae40ccf048..86d77d05190a 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -2512,8 +2512,8 @@ static void stop_nic(struct s2io_nic *nic) * Return Value: * SUCCESS on success or an appropriate -ve value on failure. */ - -static int fill_rx_buffers(struct ring_info *ring, int from_card_up) +static int fill_rx_buffers(struct s2io_nic *nic, struct ring_info *ring, + int from_card_up) { struct sk_buff *skb; struct RxD_t *rxdp; @@ -2602,7 +2602,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) rxdp1->Buffer0_ptr = pci_map_single (ring->pdev, skb->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); - if(pci_dma_mapping_error(rxdp1->Buffer0_ptr)) + if (pci_dma_mapping_error(nic->pdev, + rxdp1->Buffer0_ptr)) goto pci_map_failed; rxdp->Control_2 = @@ -2636,7 +2637,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) rxdp3->Buffer0_ptr = pci_map_single(ring->pdev, ba->ba_0, BUF0_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) + if (pci_dma_mapping_error(nic->pdev, + rxdp3->Buffer0_ptr)) goto pci_map_failed; } else pci_dma_sync_single_for_device(ring->pdev, @@ -2655,7 +2657,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) (ring->pdev, skb->data, ring->mtu + 4, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer2_ptr)) + if (pci_dma_mapping_error(nic->pdev, + rxdp3->Buffer2_ptr)) goto pci_map_failed; if (from_card_up) { @@ -2664,8 +2667,8 @@ static int fill_rx_buffers(struct ring_info *ring, int from_card_up) ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error - (rxdp3->Buffer1_ptr)) { + if (pci_dma_mapping_error(nic->pdev, + rxdp3->Buffer1_ptr)) { pci_unmap_single (ring->pdev, (dma_addr_t)(unsigned long) @@ -2806,9 +2809,9 @@ static void free_rx_buffers(struct s2io_nic *sp) } } -static int s2io_chk_rx_buffers(struct ring_info *ring) +static int s2io_chk_rx_buffers(struct s2io_nic *nic, struct ring_info *ring) { - if (fill_rx_buffers(ring, 0) == -ENOMEM) { + if (fill_rx_buffers(nic, ring, 0) == -ENOMEM) { DBG_PRINT(INFO_DBG, "%s:Out of memory", ring->dev->name); DBG_PRINT(INFO_DBG, " in Rx Intr!!\n"); } @@ -2848,7 +2851,7 @@ static int s2io_poll_msix(struct napi_struct *napi, int budget) return 0; pkts_processed = rx_intr_handler(ring, budget); - s2io_chk_rx_buffers(ring); + s2io_chk_rx_buffers(nic, ring); if (pkts_processed < budget_org) { netif_rx_complete(dev, napi); @@ -2882,7 +2885,7 @@ static int s2io_poll_inta(struct napi_struct *napi, int budget) for (i = 0; i < config->rx_ring_num; i++) { ring = &mac_control->rings[i]; ring_pkts_processed = rx_intr_handler(ring, budget); - s2io_chk_rx_buffers(ring); + s2io_chk_rx_buffers(nic, ring); pkts_processed += ring_pkts_processed; budget -= ring_pkts_processed; if (budget <= 0) @@ -2939,7 +2942,8 @@ static void s2io_netpoll(struct net_device *dev) rx_intr_handler(&mac_control->rings[i], 0); for (i = 0; i < config->rx_ring_num; i++) { - if (fill_rx_buffers(&mac_control->rings[i], 0) == -ENOMEM) { + if (fill_rx_buffers(nic, &mac_control->rings[i], 0) == + -ENOMEM) { DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); DBG_PRINT(INFO_DBG, " in Rx Netpoll!!\n"); break; @@ -4235,14 +4239,14 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) txdp->Buffer_Pointer = pci_map_single(sp->pdev, fifo->ufo_in_band_v, sizeof(u64), PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(txdp->Buffer_Pointer)) + if (pci_dma_mapping_error(sp->pdev, txdp->Buffer_Pointer)) goto pci_map_failed; txdp++; } txdp->Buffer_Pointer = pci_map_single (sp->pdev, skb->data, frg_len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(txdp->Buffer_Pointer)) + if (pci_dma_mapping_error(sp->pdev, txdp->Buffer_Pointer)) goto pci_map_failed; txdp->Host_Control = (unsigned long) skb; @@ -4345,7 +4349,7 @@ static irqreturn_t s2io_msix_ring_handle(int irq, void *dev_id) netif_rx_schedule(dev, &ring->napi); } else { rx_intr_handler(ring, 0); - s2io_chk_rx_buffers(ring); + s2io_chk_rx_buffers(sp, ring); } return IRQ_HANDLED; @@ -4826,7 +4830,7 @@ static irqreturn_t s2io_isr(int irq, void *dev_id) */ if (!config->napi) { for (i = 0; i < config->rx_ring_num; i++) - s2io_chk_rx_buffers(&mac_control->rings[i]); + s2io_chk_rx_buffers(sp, &mac_control->rings[i]); } writeq(sp->general_int_mask, &bar0->general_int_mask); readl(&bar0->general_int_status); @@ -6859,7 +6863,7 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, pci_map_single( sp->pdev, (*skb)->data, size - NET_IP_ALIGN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp1->Buffer0_ptr)) + if (pci_dma_mapping_error(sp->pdev, rxdp1->Buffer0_ptr)) goto memalloc_failed; rxdp->Host_Control = (unsigned long) (*skb); } @@ -6886,12 +6890,13 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, pci_map_single(sp->pdev, (*skb)->data, dev->mtu + 4, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer2_ptr)) + if (pci_dma_mapping_error(sp->pdev, rxdp3->Buffer2_ptr)) goto memalloc_failed; rxdp3->Buffer0_ptr = *temp0 = pci_map_single( sp->pdev, ba->ba_0, BUF0_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) { + if (pci_dma_mapping_error(sp->pdev, + rxdp3->Buffer0_ptr)) { pci_unmap_single (sp->pdev, (dma_addr_t)rxdp3->Buffer2_ptr, dev->mtu + 4, PCI_DMA_FROMDEVICE); @@ -6903,7 +6908,8 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, rxdp3->Buffer1_ptr = *temp1 = pci_map_single(sp->pdev, ba->ba_1, BUF1_LEN, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(rxdp3->Buffer1_ptr)) { + if (pci_dma_mapping_error(sp->pdev, + rxdp3->Buffer1_ptr)) { pci_unmap_single (sp->pdev, (dma_addr_t)rxdp3->Buffer0_ptr, BUF0_LEN, PCI_DMA_FROMDEVICE); @@ -7187,7 +7193,7 @@ static int s2io_card_up(struct s2io_nic * sp) for (i = 0; i < config->rx_ring_num; i++) { mac_control->rings[i].mtu = dev->mtu; - ret = fill_rx_buffers(&mac_control->rings[i], 1); + ret = fill_rx_buffers(sp, &mac_control->rings[i], 1); if (ret) { DBG_PRINT(ERR_DBG, "%s: Out of memory in Open\n", dev->name); diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c index 601b001437c0..0d27dd39bc09 100644 --- a/drivers/net/sfc/rx.c +++ b/drivers/net/sfc/rx.c @@ -233,7 +233,7 @@ static inline int efx_init_rx_buffer_skb(struct efx_rx_queue *rx_queue, rx_buf->data, rx_buf->len, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(rx_buf->dma_addr))) { + if (unlikely(pci_dma_mapping_error(efx->pci_dev, rx_buf->dma_addr))) { dev_kfree_skb_any(rx_buf->skb); rx_buf->skb = NULL; return -EIO; @@ -275,7 +275,7 @@ static inline int efx_init_rx_buffer_page(struct efx_rx_queue *rx_queue, 0, efx_rx_buf_size(efx), PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(dma_addr))) { + if (unlikely(pci_dma_mapping_error(efx->pci_dev, dma_addr))) { __free_pages(rx_buf->page, efx->rx_buffer_order); rx_buf->page = NULL; return -EIO; diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c index 5cdd082ab8f6..5e8374ab28ee 100644 --- a/drivers/net/sfc/tx.c +++ b/drivers/net/sfc/tx.c @@ -172,7 +172,7 @@ static inline int efx_enqueue_skb(struct efx_tx_queue *tx_queue, /* Process all fragments */ while (1) { - if (unlikely(pci_dma_mapping_error(dma_addr))) + if (unlikely(pci_dma_mapping_error(pci_dev, dma_addr))) goto pci_err; /* Store fields for marking in the per-fragment final @@ -661,7 +661,8 @@ efx_tsoh_heap_alloc(struct efx_tx_queue *tx_queue, size_t header_len) tsoh->dma_addr = pci_map_single(tx_queue->efx->pci_dev, TSOH_BUFFER(tsoh), header_len, PCI_DMA_TODEVICE); - if (unlikely(pci_dma_mapping_error(tsoh->dma_addr))) { + if (unlikely(pci_dma_mapping_error(tx_queue->efx->pci_dev, + tsoh->dma_addr))) { kfree(tsoh); return NULL; } @@ -863,7 +864,7 @@ static inline int tso_get_fragment(struct tso_state *st, struct efx_nic *efx, st->ifc.unmap_addr = pci_map_page(efx->pci_dev, page, page_off, len, PCI_DMA_TODEVICE); - if (likely(!pci_dma_mapping_error(st->ifc.unmap_addr))) { + if (likely(!pci_dma_mapping_error(efx->pci_dev, st->ifc.unmap_addr))) { st->ifc.unmap_len = len; st->ifc.len = len; st->ifc.dma_addr = st->ifc.unmap_addr; diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 00aa0b108cb9..b6435d0d71f9 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -452,7 +452,7 @@ spider_net_prepare_rx_descr(struct spider_net_card *card, /* iommu-map the skb */ buf = pci_map_single(card->pdev, descr->skb->data, SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(buf)) { + if (pci_dma_mapping_error(card->pdev, buf)) { dev_kfree_skb_any(descr->skb); descr->skb = NULL; if (netif_msg_rx_err(card) && net_ratelimit()) @@ -691,7 +691,7 @@ spider_net_prepare_tx_descr(struct spider_net_card *card, unsigned long flags; buf = pci_map_single(card->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(buf)) { + if (pci_dma_mapping_error(card->pdev, buf)) { if (netif_msg_tx_err(card) && net_ratelimit()) dev_err(&card->netdev->dev, "could not iommu-map packet (%p, %i). " "Dropping packet\n", skb->data, skb->len); diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index a645e5028c14..8487ace9d2e3 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -506,7 +506,7 @@ static void *alloc_rxbuf_page(struct pci_dev *hwdev, dma_addr_t *dma_handle) return NULL; *dma_handle = pci_map_single(hwdev, buf, PAGE_SIZE, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(*dma_handle)) { + if (pci_dma_mapping_error(hwdev, *dma_handle)) { free_page((unsigned long)buf); return NULL; } @@ -536,7 +536,7 @@ static struct sk_buff *alloc_rxbuf_skb(struct net_device *dev, return NULL; *dma_handle = pci_map_single(hwdev, skb->data, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(*dma_handle)) { + if (pci_dma_mapping_error(hwdev, *dma_handle)) { dev_kfree_skb_any(skb); return NULL; } diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 217d506527a9..d9769c527346 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1166,7 +1166,7 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) bf->skb = skb; bf->skbaddr = pci_map_single(sc->pdev, skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(bf->skbaddr))) { + if (unlikely(pci_dma_mapping_error(sc->pdev, bf->skbaddr))) { ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__); dev_kfree_skb(skb); bf->skb = NULL; @@ -1918,7 +1918,7 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "skb %p [data %p len %u] " "skbaddr %llx\n", skb, skb->data, skb->len, (unsigned long long)bf->skbaddr); - if (pci_dma_mapping_error(bf->skbaddr)) { + if (pci_dma_mapping_error(sc->pdev, bf->skbaddr)) { ATH5K_ERR(sc, "beacon DMA mapping failed\n"); return -EIO; } diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index c4a7c06793c5..61f8fdea2d96 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -3525,7 +3525,7 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost) crq->msg_token = dma_map_single(dev, crq->msgs, PAGE_SIZE, DMA_BIDIRECTIONAL); - if (dma_mapping_error(crq->msg_token)) + if (dma_mapping_error(dev, crq->msg_token)) goto map_failed; retrc = rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, @@ -3618,7 +3618,7 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost) async_q->size * sizeof(*async_q->msgs), DMA_BIDIRECTIONAL); - if (dma_mapping_error(async_q->msg_token)) { + if (dma_mapping_error(dev, async_q->msg_token)) { dev_err(dev, "Failed to map async queue\n"); goto free_async_crq; } diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 20000ec79b04..6b24b9cdb04c 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -859,7 +859,7 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) sizeof(hostdata->madapter_info), DMA_BIDIRECTIONAL); - if (dma_mapping_error(req->buffer)) { + if (dma_mapping_error(hostdata->dev, req->buffer)) { if (!firmware_has_feature(FW_FEATURE_CMO)) dev_err(hostdata->dev, "Unable to map request_buffer for " @@ -1407,7 +1407,7 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, length, DMA_BIDIRECTIONAL); - if (dma_mapping_error(host_config->buffer)) { + if (dma_mapping_error(hostdata->dev, host_config->buffer)) { if (!firmware_has_feature(FW_FEATURE_CMO)) dev_err(hostdata->dev, "dma_mapping error getting host config\n"); diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c index 3b9514c8f1f1..2e13ec00172a 100644 --- a/drivers/scsi/ibmvscsi/ibmvstgt.c +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -564,7 +564,7 @@ static int crq_queue_create(struct crq_queue *queue, struct srp_target *target) queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); - if (dma_mapping_error(queue->msg_token)) + if (dma_mapping_error(target->dev, queue->msg_token)) goto map_failed; err = h_reg_crq(vport->dma_dev->unit_address, queue->msg_token, diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index 182146100dc1..462a8574dad9 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -253,7 +253,7 @@ static int rpavscsi_init_crq_queue(struct crq_queue *queue, queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); - if (dma_mapping_error(queue->msg_token)) + if (dma_mapping_error(hostdata->dev, queue->msg_token)) goto map_failed; gather_partition_info(); diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index e81d59d78910..0c7165660853 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -313,14 +313,14 @@ atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer) xfer->tx_dma = dma_map_single(dev, (void *) xfer->tx_buf, xfer->len, DMA_TO_DEVICE); - if (dma_mapping_error(xfer->tx_dma)) + if (dma_mapping_error(dev, xfer->tx_dma)) return -ENOMEM; } if (xfer->rx_buf) { xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, xfer->len, DMA_FROM_DEVICE); - if (dma_mapping_error(xfer->rx_dma)) { + if (dma_mapping_error(dev, xfer->rx_dma)) { if (xfer->tx_buf) dma_unmap_single(dev, xfer->tx_dma, xfer->len, diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c index 9149689c79d9..87b73e0169c5 100644 --- a/drivers/spi/au1550_spi.c +++ b/drivers/spi/au1550_spi.c @@ -334,7 +334,7 @@ static int au1550_spi_dma_rxtmp_alloc(struct au1550_spi *hw, unsigned size) hw->dma_rx_tmpbuf_size = size; hw->dma_rx_tmpbuf_addr = dma_map_single(hw->dev, hw->dma_rx_tmpbuf, size, DMA_FROM_DEVICE); - if (dma_mapping_error(hw->dma_rx_tmpbuf_addr)) { + if (dma_mapping_error(hw->dev, hw->dma_rx_tmpbuf_addr)) { kfree(hw->dma_rx_tmpbuf); hw->dma_rx_tmpbuf = 0; hw->dma_rx_tmpbuf_size = 0; @@ -378,7 +378,7 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t) dma_rx_addr = dma_map_single(hw->dev, (void *)t->rx_buf, t->len, DMA_FROM_DEVICE); - if (dma_mapping_error(dma_rx_addr)) + if (dma_mapping_error(hw->dev, dma_rx_addr)) dev_err(hw->dev, "rx dma map error\n"); } } else { @@ -401,7 +401,7 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t) dma_tx_addr = dma_map_single(hw->dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE); - if (dma_mapping_error(dma_tx_addr)) + if (dma_mapping_error(hw->dev, dma_tx_addr)) dev_err(hw->dev, "tx dma map error\n"); } } else { diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index b1cc148036c1..f6f987bb71ca 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -836,7 +836,7 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) if (tx_buf != NULL) { t->tx_dma = dma_map_single(&spi->dev, (void *) tx_buf, len, DMA_TO_DEVICE); - if (dma_mapping_error(t->tx_dma)) { + if (dma_mapping_error(&spi->dev, t->tx_dma)) { dev_dbg(&spi->dev, "dma %cX %d bytes error\n", 'T', len); return -EINVAL; @@ -845,7 +845,7 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) if (rx_buf != NULL) { t->rx_dma = dma_map_single(&spi->dev, rx_buf, t->len, DMA_FROM_DEVICE); - if (dma_mapping_error(t->rx_dma)) { + if (dma_mapping_error(&spi->dev, t->rx_dma)) { dev_dbg(&spi->dev, "dma %cX %d bytes error\n", 'R', len); if (tx_buf != NULL) diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 0c452c46ab07..067299d6d192 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -353,7 +353,7 @@ static int map_dma_buffers(struct driver_data *drv_data) drv_data->rx_dma = dma_map_single(dev, drv_data->rx, drv_data->rx_map_len, DMA_FROM_DEVICE); - if (dma_mapping_error(drv_data->rx_dma)) + if (dma_mapping_error(dev, drv_data->rx_dma)) return 0; /* Stream map the tx buffer */ @@ -361,7 +361,7 @@ static int map_dma_buffers(struct driver_data *drv_data) drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(drv_data->tx_dma)) { + if (dma_mapping_error(dev, drv_data->tx_dma)) { dma_unmap_single(dev, drv_data->rx_dma, drv_data->rx_map_len, DMA_FROM_DEVICE); return 0; diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 54ac7bea5f8c..6fb77fcc4971 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -491,7 +491,7 @@ static int map_dma_buffers(struct driver_data *drv_data) buf, drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(drv_data->tx_dma)) + if (dma_mapping_error(dev, drv_data->tx_dma)) return -1; drv_data->tx_dma_needs_unmap = 1; @@ -516,7 +516,7 @@ static int map_dma_buffers(struct driver_data *drv_data) buf, drv_data->len, DMA_FROM_DEVICE); - if (dma_mapping_error(drv_data->rx_dma)) + if (dma_mapping_error(dev, drv_data->rx_dma)) return -1; drv_data->rx_dma_needs_unmap = 1; } @@ -534,7 +534,7 @@ static int map_dma_buffers(struct driver_data *drv_data) buf, drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(drv_data->tx_dma)) { + if (dma_mapping_error(dev, drv_data->tx_dma)) { if (drv_data->rx_dma) { dma_unmap_single(dev, drv_data->rx_dma, diff --git a/include/asm-alpha/dma-mapping.h b/include/asm-alpha/dma-mapping.h index db351d1296f4..a5801ae02e4b 100644 --- a/include/asm-alpha/dma-mapping.h +++ b/include/asm-alpha/dma-mapping.h @@ -24,8 +24,8 @@ pci_unmap_sg(alpha_gendev_to_pci(dev), sg, nents, dir) #define dma_supported(dev, mask) \ pci_dma_supported(alpha_gendev_to_pci(dev), mask) -#define dma_mapping_error(addr) \ - pci_dma_mapping_error(addr) +#define dma_mapping_error(dev, addr) \ + pci_dma_mapping_error(alpha_gendev_to_pci(dev), addr) #else /* no PCI - no IOMMU. */ @@ -45,7 +45,7 @@ int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, #define dma_unmap_page(dev, addr, size, dir) ((void)0) #define dma_unmap_sg(dev, sg, nents, dir) ((void)0) -#define dma_mapping_error(addr) (0) +#define dma_mapping_error(dev, addr) (0) #endif /* !CONFIG_PCI */ diff --git a/include/asm-alpha/pci.h b/include/asm-alpha/pci.h index d31fd49ff79a..2a14302c17a3 100644 --- a/include/asm-alpha/pci.h +++ b/include/asm-alpha/pci.h @@ -106,7 +106,7 @@ extern dma_addr_t pci_map_page(struct pci_dev *, struct page *, /* Test for pci_map_single or pci_map_page having generated an error. */ static inline int -pci_dma_mapping_error(dma_addr_t dma_addr) +pci_dma_mapping_error(struct pci_dev *pdev, dma_addr_t dma_addr) { return dma_addr == 0; } diff --git a/include/asm-arm/dma-mapping.h b/include/asm-arm/dma-mapping.h index e99406a7bece..f41335ba6337 100644 --- a/include/asm-arm/dma-mapping.h +++ b/include/asm-arm/dma-mapping.h @@ -56,7 +56,7 @@ static inline int dma_is_consistent(struct device *dev, dma_addr_t handle) /* * DMA errors are defined by all-bits-set in the DMA address. */ -static inline int dma_mapping_error(dma_addr_t dma_addr) +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return dma_addr == ~0; } diff --git a/include/asm-avr32/dma-mapping.h b/include/asm-avr32/dma-mapping.h index 57dc672bab8e..0399359ab5d8 100644 --- a/include/asm-avr32/dma-mapping.h +++ b/include/asm-avr32/dma-mapping.h @@ -35,7 +35,7 @@ static inline int dma_set_mask(struct device *dev, u64 dma_mask) /* * dma_map_single can't fail as it is implemented now. */ -static inline int dma_mapping_error(dma_addr_t addr) +static inline int dma_mapping_error(struct device *dev, dma_addr_t addr) { return 0; } diff --git a/include/asm-cris/dma-mapping.h b/include/asm-cris/dma-mapping.h index edc8d1bfaae2..cb2fb25ff8d9 100644 --- a/include/asm-cris/dma-mapping.h +++ b/include/asm-cris/dma-mapping.h @@ -120,7 +120,7 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, } static inline int -dma_mapping_error(dma_addr_t dma_addr) +dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/include/asm-frv/dma-mapping.h b/include/asm-frv/dma-mapping.h index 2e8966ca030d..b2898877c07b 100644 --- a/include/asm-frv/dma-mapping.h +++ b/include/asm-frv/dma-mapping.h @@ -126,7 +126,7 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nele } static inline -int dma_mapping_error(dma_addr_t dma_addr) +int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/include/asm-generic/dma-mapping-broken.h b/include/asm-generic/dma-mapping-broken.h index e2468f894d2a..82cd0cb1c3fe 100644 --- a/include/asm-generic/dma-mapping-broken.h +++ b/include/asm-generic/dma-mapping-broken.h @@ -61,7 +61,7 @@ dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, #define dma_sync_sg_for_device dma_sync_sg_for_cpu extern int -dma_mapping_error(dma_addr_t dma_addr); +dma_mapping_error(struct device *dev, dma_addr_t dma_addr); extern int dma_supported(struct device *dev, u64 mask); diff --git a/include/asm-generic/dma-mapping.h b/include/asm-generic/dma-mapping.h index 783ab9944d70..189486c3f92e 100644 --- a/include/asm-generic/dma-mapping.h +++ b/include/asm-generic/dma-mapping.h @@ -144,9 +144,9 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, } static inline int -dma_mapping_error(dma_addr_t dma_addr) +dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { - return pci_dma_mapping_error(dma_addr); + return pci_dma_mapping_error(to_pci_dev(dev), dma_addr); } diff --git a/include/asm-generic/pci-dma-compat.h b/include/asm-generic/pci-dma-compat.h index 25c10e96b2b7..37b3706226e7 100644 --- a/include/asm-generic/pci-dma-compat.h +++ b/include/asm-generic/pci-dma-compat.h @@ -99,9 +99,9 @@ pci_dma_sync_sg_for_device(struct pci_dev *hwdev, struct scatterlist *sg, } static inline int -pci_dma_mapping_error(dma_addr_t dma_addr) +pci_dma_mapping_error(struct pci_dev *pdev, dma_addr_t dma_addr) { - return dma_mapping_error(dma_addr); + return dma_mapping_error(&pdev->dev, dma_addr); } #endif diff --git a/include/asm-ia64/machvec.h b/include/asm-ia64/machvec.h index 0721a5e8271e..a6d50c77b6bf 100644 --- a/include/asm-ia64/machvec.h +++ b/include/asm-ia64/machvec.h @@ -54,7 +54,7 @@ typedef void ia64_mv_dma_sync_single_for_cpu (struct device *, dma_addr_t, size_ typedef void ia64_mv_dma_sync_sg_for_cpu (struct device *, struct scatterlist *, int, int); typedef void ia64_mv_dma_sync_single_for_device (struct device *, dma_addr_t, size_t, int); typedef void ia64_mv_dma_sync_sg_for_device (struct device *, struct scatterlist *, int, int); -typedef int ia64_mv_dma_mapping_error (dma_addr_t dma_addr); +typedef int ia64_mv_dma_mapping_error(struct device *, dma_addr_t dma_addr); typedef int ia64_mv_dma_supported (struct device *, u64); typedef dma_addr_t ia64_mv_dma_map_single_attrs (struct device *, void *, size_t, int, struct dma_attrs *); diff --git a/include/asm-m68k/dma-mapping.h b/include/asm-m68k/dma-mapping.h index a26cdeb46a57..91f7944333d4 100644 --- a/include/asm-m68k/dma-mapping.h +++ b/include/asm-m68k/dma-mapping.h @@ -84,7 +84,7 @@ static inline void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *s { } -static inline int dma_mapping_error(dma_addr_t handle) +static inline int dma_mapping_error(struct device *dev, dma_addr_t handle) { return 0; } diff --git a/include/asm-mips/dma-mapping.h b/include/asm-mips/dma-mapping.h index 230b3f1b69b1..c64afb40cd06 100644 --- a/include/asm-mips/dma-mapping.h +++ b/include/asm-mips/dma-mapping.h @@ -42,7 +42,7 @@ extern void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction); extern void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction direction); -extern int dma_mapping_error(dma_addr_t dma_addr); +extern int dma_mapping_error(struct device *dev, dma_addr_t dma_addr); extern int dma_supported(struct device *dev, u64 mask); static inline int diff --git a/include/asm-mn10300/dma-mapping.h b/include/asm-mn10300/dma-mapping.h index 7c882fca9ec8..ccae8f6c6326 100644 --- a/include/asm-mn10300/dma-mapping.h +++ b/include/asm-mn10300/dma-mapping.h @@ -182,7 +182,7 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, } static inline -int dma_mapping_error(dma_addr_t dma_addr) +int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/include/asm-parisc/dma-mapping.h b/include/asm-parisc/dma-mapping.h index c6c0e9ff6bde..53af696f23d2 100644 --- a/include/asm-parisc/dma-mapping.h +++ b/include/asm-parisc/dma-mapping.h @@ -248,6 +248,6 @@ void * sba_get_iommu(struct parisc_device *dev); #endif /* At the moment, we panic on error for IOMMU resource exaustion */ -#define dma_mapping_error(x) 0 +#define dma_mapping_error(dev, x) 0 #endif diff --git a/include/asm-powerpc/dma-mapping.h b/include/asm-powerpc/dma-mapping.h index 74c549780987..c7ca45f97dd2 100644 --- a/include/asm-powerpc/dma-mapping.h +++ b/include/asm-powerpc/dma-mapping.h @@ -415,7 +415,7 @@ static inline void dma_sync_sg_for_device(struct device *dev, __dma_sync_page(sg_page(sg), sg->offset, sg->length, direction); } -static inline int dma_mapping_error(dma_addr_t dma_addr) +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { #ifdef CONFIG_PPC64 return (dma_addr == DMA_ERROR_CODE); diff --git a/include/asm-sh/dma-mapping.h b/include/asm-sh/dma-mapping.h index 22cc419389fe..6c0b8a2de143 100644 --- a/include/asm-sh/dma-mapping.h +++ b/include/asm-sh/dma-mapping.h @@ -171,7 +171,7 @@ static inline int dma_get_cache_alignment(void) return L1_CACHE_BYTES; } -static inline int dma_mapping_error(dma_addr_t dma_addr) +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return dma_addr == 0; } diff --git a/include/asm-sparc/dma-mapping_64.h b/include/asm-sparc/dma-mapping_64.h index 38cbec76a33f..bfa64f9702d5 100644 --- a/include/asm-sparc/dma-mapping_64.h +++ b/include/asm-sparc/dma-mapping_64.h @@ -135,7 +135,7 @@ static inline void dma_sync_sg_for_device(struct device *dev, /* No flushing needed to sync cpu writes to the device. */ } -static inline int dma_mapping_error(dma_addr_t dma_addr) +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return (dma_addr == DMA_ERROR_CODE); } diff --git a/include/asm-sparc/pci_32.h b/include/asm-sparc/pci_32.h index b93b6c79e08f..0ee949d220c0 100644 --- a/include/asm-sparc/pci_32.h +++ b/include/asm-sparc/pci_32.h @@ -154,7 +154,8 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, #define PCI_DMA_ERROR_CODE (~(dma_addr_t)0x0) -static inline int pci_dma_mapping_error(dma_addr_t dma_addr) +static inline int pci_dma_mapping_error(struct pci_dev *pdev, + dma_addr_t dma_addr) { return (dma_addr == PCI_DMA_ERROR_CODE); } diff --git a/include/asm-sparc/pci_64.h b/include/asm-sparc/pci_64.h index f59f2571295b..4f79a54948f6 100644 --- a/include/asm-sparc/pci_64.h +++ b/include/asm-sparc/pci_64.h @@ -140,9 +140,10 @@ extern int pci_dma_supported(struct pci_dev *hwdev, u64 mask); #define PCI64_REQUIRED_MASK (~(dma64_addr_t)0) #define PCI64_ADDR_BASE 0xfffc000000000000UL -static inline int pci_dma_mapping_error(dma_addr_t dma_addr) +static inline int pci_dma_mapping_error(struct pci_dev *pdev, + dma_addr_t dma_addr) { - return dma_mapping_error(dma_addr); + return dma_mapping_error(&pdev->dev, dma_addr); } #ifdef CONFIG_PCI diff --git a/include/asm-x86/device.h b/include/asm-x86/device.h index 87a715367a1b..3c034f48fdb0 100644 --- a/include/asm-x86/device.h +++ b/include/asm-x86/device.h @@ -5,6 +5,9 @@ struct dev_archdata { #ifdef CONFIG_ACPI void *acpi_handle; #endif +#ifdef CONFIG_X86_64 +struct dma_mapping_ops *dma_ops; +#endif #ifdef CONFIG_DMAR void *iommu; /* hook for IOMMU specific extension */ #endif diff --git a/include/asm-x86/dma-mapping.h b/include/asm-x86/dma-mapping.h index c2ddd3d1b883..0eaa9bf6011f 100644 --- a/include/asm-x86/dma-mapping.h +++ b/include/asm-x86/dma-mapping.h @@ -17,7 +17,8 @@ extern int panic_on_overflow; extern int force_iommu; struct dma_mapping_ops { - int (*mapping_error)(dma_addr_t dma_addr); + int (*mapping_error)(struct device *dev, + dma_addr_t dma_addr); void* (*alloc_coherent)(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp); void (*free_coherent)(struct device *dev, size_t size, @@ -56,14 +57,32 @@ struct dma_mapping_ops { int is_phys; }; -extern const struct dma_mapping_ops *dma_ops; +extern struct dma_mapping_ops *dma_ops; -static inline int dma_mapping_error(dma_addr_t dma_addr) +static inline struct dma_mapping_ops *get_dma_ops(struct device *dev) { - if (dma_ops->mapping_error) - return dma_ops->mapping_error(dma_addr); +#ifdef CONFIG_X86_32 + return dma_ops; +#else + if (unlikely(!dev) || !dev->archdata.dma_ops) + return dma_ops; + else + return dev->archdata.dma_ops; +#endif +} + +/* Make sure we keep the same behaviour */ +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ +#ifdef CONFIG_X86_32 + return 0; +#else + struct dma_mapping_ops *ops = get_dma_ops(dev); + if (ops->mapping_error) + return ops->mapping_error(dev, dma_addr); return (dma_addr == bad_dma_address); +#endif } #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) @@ -83,44 +102,53 @@ static inline dma_addr_t dma_map_single(struct device *hwdev, void *ptr, size_t size, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - return dma_ops->map_single(hwdev, virt_to_phys(ptr), size, direction); + return ops->map_single(hwdev, virt_to_phys(ptr), size, direction); } static inline void dma_unmap_single(struct device *dev, dma_addr_t addr, size_t size, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(dev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->unmap_single) - dma_ops->unmap_single(dev, addr, size, direction); + if (ops->unmap_single) + ops->unmap_single(dev, addr, size, direction); } static inline int dma_map_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - return dma_ops->map_sg(hwdev, sg, nents, direction); + return ops->map_sg(hwdev, sg, nents, direction); } static inline void dma_unmap_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->unmap_sg) - dma_ops->unmap_sg(hwdev, sg, nents, direction); + if (ops->unmap_sg) + ops->unmap_sg(hwdev, sg, nents, direction); } static inline void dma_sync_single_for_cpu(struct device *hwdev, dma_addr_t dma_handle, size_t size, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_single_for_cpu) - dma_ops->sync_single_for_cpu(hwdev, dma_handle, size, - direction); + if (ops->sync_single_for_cpu) + ops->sync_single_for_cpu(hwdev, dma_handle, size, direction); flush_write_buffers(); } @@ -128,10 +156,11 @@ static inline void dma_sync_single_for_device(struct device *hwdev, dma_addr_t dma_handle, size_t size, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_single_for_device) - dma_ops->sync_single_for_device(hwdev, dma_handle, size, - direction); + if (ops->sync_single_for_device) + ops->sync_single_for_device(hwdev, dma_handle, size, direction); flush_write_buffers(); } @@ -139,11 +168,12 @@ static inline void dma_sync_single_range_for_cpu(struct device *hwdev, dma_addr_t dma_handle, unsigned long offset, size_t size, int direction) { - BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_single_range_for_cpu) - dma_ops->sync_single_range_for_cpu(hwdev, dma_handle, offset, - size, direction); + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); + if (ops->sync_single_range_for_cpu) + ops->sync_single_range_for_cpu(hwdev, dma_handle, offset, + size, direction); flush_write_buffers(); } @@ -152,11 +182,12 @@ dma_sync_single_range_for_device(struct device *hwdev, dma_addr_t dma_handle, unsigned long offset, size_t size, int direction) { - BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_single_range_for_device) - dma_ops->sync_single_range_for_device(hwdev, dma_handle, - offset, size, direction); + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); + if (ops->sync_single_range_for_device) + ops->sync_single_range_for_device(hwdev, dma_handle, + offset, size, direction); flush_write_buffers(); } @@ -164,9 +195,11 @@ static inline void dma_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg, int nelems, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_sg_for_cpu) - dma_ops->sync_sg_for_cpu(hwdev, sg, nelems, direction); + if (ops->sync_sg_for_cpu) + ops->sync_sg_for_cpu(hwdev, sg, nelems, direction); flush_write_buffers(); } @@ -174,9 +207,11 @@ static inline void dma_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, int nelems, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(hwdev); + BUG_ON(!valid_dma_direction(direction)); - if (dma_ops->sync_sg_for_device) - dma_ops->sync_sg_for_device(hwdev, sg, nelems, direction); + if (ops->sync_sg_for_device) + ops->sync_sg_for_device(hwdev, sg, nelems, direction); flush_write_buffers(); } @@ -185,9 +220,11 @@ static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, size_t offset, size_t size, int direction) { + struct dma_mapping_ops *ops = get_dma_ops(dev); + BUG_ON(!valid_dma_direction(direction)); - return dma_ops->map_single(dev, page_to_phys(page)+offset, - size, direction); + return ops->map_single(dev, page_to_phys(page) + offset, + size, direction); } static inline void dma_unmap_page(struct device *dev, dma_addr_t addr, diff --git a/include/asm-x86/swiotlb.h b/include/asm-x86/swiotlb.h index c706a7442633..2730b351afcf 100644 --- a/include/asm-x86/swiotlb.h +++ b/include/asm-x86/swiotlb.h @@ -35,7 +35,7 @@ extern int swiotlb_map_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction); extern void swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction); -extern int swiotlb_dma_mapping_error(dma_addr_t dma_addr); +extern int swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr); extern void swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle); extern int swiotlb_dma_supported(struct device *hwdev, u64 mask); diff --git a/include/asm-xtensa/dma-mapping.h b/include/asm-xtensa/dma-mapping.h index 3c7d537dd15d..51882ae3db4d 100644 --- a/include/asm-xtensa/dma-mapping.h +++ b/include/asm-xtensa/dma-mapping.h @@ -139,7 +139,7 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, consistent_sync(sg_virt(sg), sg->length, dir); } static inline int -dma_mapping_error(dma_addr_t dma_addr) +dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { return 0; } diff --git a/include/linux/i2o.h b/include/linux/i2o.h index 7d51cbca49ab..75ae6d8aba4f 100644 --- a/include/linux/i2o.h +++ b/include/linux/i2o.h @@ -758,7 +758,7 @@ static inline dma_addr_t i2o_dma_map_single(struct i2o_controller *c, void *ptr, } dma_addr = dma_map_single(&c->pdev->dev, ptr, size, direction); - if (!dma_mapping_error(dma_addr)) { + if (!dma_mapping_error(&c->pdev->dev, dma_addr)) { #ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64 if ((sizeof(dma_addr_t) > 4) && c->pae_support) { *mptr++ = cpu_to_le32(0x7C020002); diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 4bf8cade9dbc..e530026eedf7 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -427,9 +427,9 @@ static inline int ssb_dma_mapping_error(struct ssb_device *dev, dma_addr_t addr) { switch (dev->bus->bustype) { case SSB_BUSTYPE_PCI: - return pci_dma_mapping_error(addr); + return pci_dma_mapping_error(dev->bus->host_pci, addr); case SSB_BUSTYPE_SSB: - return dma_mapping_error(addr); + return dma_mapping_error(dev->dev, addr); default: __ssb_dma_not_implemented(dev); } diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 90b529f7a154..936e333e7ce5 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1590,7 +1590,7 @@ static inline int ib_dma_mapping_error(struct ib_device *dev, u64 dma_addr) { if (dev->dma_ops) return dev->dma_ops->mapping_error(dev, dma_addr); - return dma_mapping_error(dma_addr); + return dma_mapping_error(dev->dma_device, dma_addr); } /** diff --git a/lib/swiotlb.c b/lib/swiotlb.c index d568894df8cc..977edbdbc1de 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c @@ -492,7 +492,7 @@ swiotlb_alloc_coherent(struct device *hwdev, size_t size, */ dma_addr_t handle; handle = swiotlb_map_single(NULL, NULL, size, DMA_FROM_DEVICE); - if (swiotlb_dma_mapping_error(handle)) + if (swiotlb_dma_mapping_error(hwdev, handle)) return NULL; ret = bus_to_virt(handle); @@ -824,7 +824,7 @@ swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, } int -swiotlb_dma_mapping_error(dma_addr_t dma_addr) +swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr) { return (dma_addr == virt_to_bus(io_tlb_overflow_buffer)); } diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index a19b22b452a3..84d328329d98 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -169,7 +169,8 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, (void *) vec->sge[xdr_sge_no].iov_base + sge_off, sge_bytes, DMA_TO_DEVICE); - if (dma_mapping_error(sge[sge_no].addr)) + if (dma_mapping_error(xprt->sc_cm_id->device->dma_device, + sge[sge_no].addr)) goto err; sge_off = 0; sge_no++; -- cgit From 929dfb24fbcd60e2544b2de7bfb4a68da4dfc747 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 19:44:54 -0700 Subject: parport/share.c: proper externs This patch adds proper externs for parport_default_timeslice and parport_default_spintime in include/linux/parport.h Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/parport/procfs.c | 3 --- include/linux/parport.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index d950fc34320a..554e11f9e1ce 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -429,9 +429,6 @@ struct parport_default_sysctl_table ctl_table dev_dir[2]; }; -extern unsigned long parport_default_timeslice; -extern int parport_default_spintime; - static struct parport_default_sysctl_table parport_default_sysctl_table = { .sysctl_header = NULL, diff --git a/include/linux/parport.h b/include/linux/parport.h index dcb9e01a69ca..6a0d7cdb5774 100644 --- a/include/linux/parport.h +++ b/include/linux/parport.h @@ -560,5 +560,8 @@ extern int parport_device_proc_unregister(struct pardevice *device); #endif /* !CONFIG_PARPORT_NOT_PC */ +extern unsigned long parport_default_timeslice; +extern int parport_default_spintime; + #endif /* __KERNEL__ */ #endif /* _PARPORT_H_ */ -- cgit From b77899985bdfd85a8e5a6e485033a9b4713d2471 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 25 Jul 2008 19:45:00 -0700 Subject: memstick: allow "set_param" method to return an error code Some controllers (Jmicron, for instance) can report temporal failure condition during power-on. It is desirable to account for this using a return value of "set_param" device method. The return value can also be handy to distinguish between supported and unsupported device parameters in run time. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Alex Dubov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/memstick/core/memstick.c | 18 ++++++++--- drivers/memstick/host/jmb38x_ms.c | 67 ++++++++++++++++++++++++++++----------- drivers/memstick/host/tifm_ms.c | 17 +++++----- include/linux/memstick.h | 2 +- 4 files changed, 71 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index 61b98c333cb0..3c7d9a79c1ea 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -415,10 +415,14 @@ err_out: return NULL; } -static void memstick_power_on(struct memstick_host *host) +static int memstick_power_on(struct memstick_host *host) { - host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON); - host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL); + int rc = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON); + + if (!rc) + rc = host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL); + + return rc; } static void memstick_check(struct work_struct *work) @@ -573,11 +577,15 @@ EXPORT_SYMBOL(memstick_suspend_host); */ void memstick_resume_host(struct memstick_host *host) { + int rc = 0; + mutex_lock(&host->lock); if (host->card) - memstick_power_on(host); + rc = memstick_power_on(host); mutex_unlock(&host->lock); - memstick_detect_change(host); + + if (!rc) + memstick_detect_change(host); } EXPORT_SYMBOL(memstick_resume_host); diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c index 4e3bfbcdf155..9d82e67737db 100644 --- a/drivers/memstick/host/jmb38x_ms.c +++ b/drivers/memstick/host/jmb38x_ms.c @@ -609,36 +609,68 @@ static void jmb38x_ms_request(struct memstick_host *msh) spin_unlock_irqrestore(&host->lock, flags); } -static void jmb38x_ms_reset(struct jmb38x_ms_host *host) +static int jmb38x_ms_reset(struct jmb38x_ms_host *host) { - unsigned int host_ctl = readl(host->addr + HOST_CONTROL); + int cnt; - writel(HOST_CONTROL_RESET_REQ, host->addr + HOST_CONTROL); + writel(HOST_CONTROL_RESET_REQ | HOST_CONTROL_CLOCK_EN + | readl(host->addr + HOST_CONTROL), + host->addr + HOST_CONTROL); + mmiowb(); + + for (cnt = 0; cnt < 20; ++cnt) { + if (!(HOST_CONTROL_RESET_REQ + & readl(host->addr + HOST_CONTROL))) + goto reset_next; - while (HOST_CONTROL_RESET_REQ - & (host_ctl = readl(host->addr + HOST_CONTROL))) { ndelay(20); - dev_dbg(&host->chip->pdev->dev, "reset %08x\n", host_ctl); } + dev_dbg(&host->chip->pdev->dev, "reset_req timeout\n"); + return -EIO; - writel(HOST_CONTROL_RESET, host->addr + HOST_CONTROL); +reset_next: + writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN + | readl(host->addr + HOST_CONTROL), + host->addr + HOST_CONTROL); + mmiowb(); + + for (cnt = 0; cnt < 20; ++cnt) { + if (!(HOST_CONTROL_RESET + & readl(host->addr + HOST_CONTROL))) + goto reset_ok; + + ndelay(20); + } + dev_dbg(&host->chip->pdev->dev, "reset timeout\n"); + return -EIO; + +reset_ok: mmiowb(); writel(INT_STATUS_ALL, host->addr + INT_SIGNAL_ENABLE); writel(INT_STATUS_ALL, host->addr + INT_STATUS_ENABLE); + return 0; } -static void jmb38x_ms_set_param(struct memstick_host *msh, - enum memstick_param param, - int value) +static int jmb38x_ms_set_param(struct memstick_host *msh, + enum memstick_param param, + int value) { struct jmb38x_ms_host *host = memstick_priv(msh); unsigned int host_ctl = readl(host->addr + HOST_CONTROL); unsigned int clock_ctl = CLOCK_CONTROL_40MHZ, clock_delay = 0; + int rc = 0; switch (param) { case MEMSTICK_POWER: if (value == MEMSTICK_POWER_ON) { - jmb38x_ms_reset(host); + rc = jmb38x_ms_reset(host); + if (rc) + return rc; + + host_ctl = 7; + host_ctl |= HOST_CONTROL_POWER_EN + | HOST_CONTROL_CLOCK_EN; + writel(host_ctl, host->addr + HOST_CONTROL); writel(host->id ? PAD_PU_PD_ON_MS_SOCK1 : PAD_PU_PD_ON_MS_SOCK0, @@ -647,11 +679,7 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, writel(PAD_OUTPUT_ENABLE_MS, host->addr + PAD_OUTPUT_ENABLE); - host_ctl = 7; - host_ctl |= HOST_CONTROL_POWER_EN - | HOST_CONTROL_CLOCK_EN; - writel(host_ctl, host->addr + HOST_CONTROL); - + msleep(10); dev_dbg(&host->chip->pdev->dev, "power on\n"); } else if (value == MEMSTICK_POWER_OFF) { host_ctl &= ~(HOST_CONTROL_POWER_EN @@ -660,7 +688,8 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, writel(0, host->addr + PAD_OUTPUT_ENABLE); writel(PAD_PU_PD_OFF, host->addr + PAD_PU_PD); dev_dbg(&host->chip->pdev->dev, "power off\n"); - } + } else + return -EINVAL; break; case MEMSTICK_INTERFACE: host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT); @@ -686,12 +715,14 @@ static void jmb38x_ms_set_param(struct memstick_host *msh, host_ctl &= ~HOST_CONTROL_REI; clock_ctl = CLOCK_CONTROL_60MHZ; clock_delay = 0; - } + } else + return -EINVAL; writel(host_ctl, host->addr + HOST_CONTROL); writel(clock_ctl, host->addr + CLOCK_CONTROL); writel(clock_delay, host->addr + CLOCK_DELAY); break; }; + return 0; } #ifdef CONFIG_PM diff --git a/drivers/memstick/host/tifm_ms.c b/drivers/memstick/host/tifm_ms.c index 8577de4ebb0e..14458764588c 100644 --- a/drivers/memstick/host/tifm_ms.c +++ b/drivers/memstick/host/tifm_ms.c @@ -489,15 +489,12 @@ static void tifm_ms_request(struct memstick_host *msh) return; } -static void tifm_ms_set_param(struct memstick_host *msh, - enum memstick_param param, - int value) +static int tifm_ms_set_param(struct memstick_host *msh, + enum memstick_param param, + int value) { struct tifm_ms *host = memstick_priv(msh); struct tifm_dev *sock = host->dev; - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); switch (param) { case MEMSTICK_POWER: @@ -512,7 +509,8 @@ static void tifm_ms_set_param(struct memstick_host *msh, writel(TIFM_MS_SYS_FCLR | TIFM_MS_SYS_INTCLR, sock->addr + SOCK_MS_SYSTEM); writel(0xffffffff, sock->addr + SOCK_MS_STATUS); - } + } else + return -EINVAL; break; case MEMSTICK_INTERFACE: if (value == MEMSTICK_SERIAL) { @@ -525,11 +523,12 @@ static void tifm_ms_set_param(struct memstick_host *msh, writel(TIFM_CTRL_FAST_CLK | readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); - } + } else + return -EINVAL; break; }; - spin_unlock_irqrestore(&sock->lock, flags); + return 0; } static void tifm_ms_abort(unsigned long data) diff --git a/include/linux/memstick.h b/include/linux/memstick.h index 37a5cdb03918..2fe599c66d52 100644 --- a/include/linux/memstick.h +++ b/include/linux/memstick.h @@ -284,7 +284,7 @@ struct memstick_host { /* Notify the host that some requests are pending. */ void (*request)(struct memstick_host *host); /* Set host IO parameters (power, clock, etc). */ - void (*set_param)(struct memstick_host *host, + int (*set_param)(struct memstick_host *host, enum memstick_param param, int value); unsigned long private[0] ____cacheline_aligned; -- cgit From 17017d8d2c005734d7088d8281ce2daab8fcb097 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 25 Jul 2008 19:45:01 -0700 Subject: memstick: add "start" and "stop" methods to memstick device In some cases it may be desirable to ensure that associated driver is not going to access the media in some period of time. "start" and "stop" methods are provided therefore to allow it. Signed-off-by: Alex Dubov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/memstick/core/memstick.c | 11 ++++++++--- drivers/memstick/core/mspro_block.c | 33 +++++++++++++++++++++++++++++++++ include/linux/memstick.h | 4 ++++ 3 files changed, 45 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index 3c7d9a79c1ea..7162f772bbfb 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -433,8 +433,11 @@ static void memstick_check(struct work_struct *work) dev_dbg(&host->dev, "memstick_check started\n"); mutex_lock(&host->lock); - if (!host->card) - memstick_power_on(host); + if (!host->card) { + if (memstick_power_on(host)) + goto out_power_off; + } else + host->card->stop(host->card); card = memstick_alloc_card(host); @@ -452,7 +455,8 @@ static void memstick_check(struct work_struct *work) || !(host->card->check(host->card))) { device_unregister(&host->card->dev); host->card = NULL; - } + } else + host->card->start(host->card); } if (!host->card) { @@ -465,6 +469,7 @@ static void memstick_check(struct work_struct *work) kfree(card); } +out_power_off: if (!host->card) host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index 477d0fb6e588..004ac4d176d9 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -752,6 +752,37 @@ static int mspro_block_has_request(struct mspro_block_data *msb) return rc; } +static void mspro_block_stop(struct memstick_dev *card) +{ + struct mspro_block_data *msb = memstick_get_drvdata(card); + int rc = 0; + unsigned long flags; + + while (1) { + spin_lock_irqsave(&msb->q_lock, flags); + if (!msb->has_request) { + blk_stop_queue(msb->queue); + rc = 1; + } + spin_unlock_irqrestore(&msb->q_lock, flags); + + if (rc) + break; + + wait_for_completion(&card->mrq_complete); + } +} + +static void mspro_block_start(struct memstick_dev *card) +{ + struct mspro_block_data *msb = memstick_get_drvdata(card); + unsigned long flags; + + spin_lock_irqsave(&msb->q_lock, flags); + blk_start_queue(msb->queue); + spin_unlock_irqrestore(&msb->q_lock, flags); +} + static int mspro_block_queue_thread(void *data) { struct memstick_dev *card = data; @@ -1272,6 +1303,8 @@ static int mspro_block_probe(struct memstick_dev *card) rc = mspro_block_init_disk(card); if (!rc) { card->check = mspro_block_check_card; + card->stop = mspro_block_stop; + card->start = mspro_block_start; return 0; } diff --git a/include/linux/memstick.h b/include/linux/memstick.h index 2fe599c66d52..a9f998a3f48b 100644 --- a/include/linux/memstick.h +++ b/include/linux/memstick.h @@ -263,6 +263,10 @@ struct memstick_dev { /* Get next request from the media driver. */ int (*next_request)(struct memstick_dev *card, struct memstick_request **mrq); + /* Tell the media driver to stop doing things */ + void (*stop)(struct memstick_dev *card); + /* Allow the media driver to continue */ + void (*start)(struct memstick_dev *card); struct device dev; }; -- cgit From 3ab83521378268044a448113c6aa9a9e245f4d2f Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Fri, 25 Jul 2008 19:45:07 -0700 Subject: kexec jump This patch provides an enhancement to kexec/kdump. It implements the following features: - Backup/restore memory used by the original kernel before/after kexec. - Save/restore CPU state before/after kexec. The features of this patch can be used as a general method to call program in physical mode (paging turning off). This can be used to call BIOS code under Linux. kexec-tools needs to be patched to support kexec jump. The patches and the precompiled kexec can be download from the following URL: source: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec-tools-src_git_kh10.tar.bz2 patches: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec-tools-patches_git_kh10.tar.bz2 binary: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec_git_kh10 Usage example of calling some physical mode code and return: 1. Compile and install patched kernel with following options selected: CONFIG_X86_32=y CONFIG_KEXEC=y CONFIG_PM=y CONFIG_KEXEC_JUMP=y 2. Build patched kexec-tool or download the pre-built one. 3. Build some physical mode executable named such as "phy_mode" 4. Boot kernel compiled in step 1. 5. Load physical mode executable with /sbin/kexec. The shell command line can be as follow: /sbin/kexec --load-preserve-context --args-none phy_mode 6. Call physical mode executable with following shell command line: /sbin/kexec -e Implementation point: To support jumping without reserving memory. One shadow backup page (source page) is allocated for each page used by kexeced code image (destination page). When do kexec_load, the image of kexeced code is loaded into source pages, and before executing, the destination pages and the source pages are swapped, so the contents of destination pages are backupped. Before jumping to the kexeced code image and after jumping back to the original kernel, the destination pages and the source pages are swapped too. C ABI (calling convention) is used as communication protocol between kernel and called code. A flag named KEXEC_PRESERVE_CONTEXT for sys_kexec_load is added to indicate that the loaded kernel image is used for jumping back. Now, only the i386 architecture is supported. Signed-off-by: Huang Ying Acked-by: Vivek Goyal Cc: "Eric W. Biederman" Cc: Pavel Machek Cc: Nigel Cunningham Cc: "Rafael J. Wysocki" Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/kernel/machine_kexec.c | 2 +- arch/sh/kernel/machine_kexec.c | 2 +- arch/x86/Kconfig | 7 ++ arch/x86/kernel/machine_kexec_32.c | 27 ++++-- arch/x86/kernel/machine_kexec_64.c | 2 +- arch/x86/kernel/relocate_kernel_32.S | 174 ++++++++++++++++++++++++++++++----- include/asm-x86/kexec.h | 18 ++-- include/linux/kexec.h | 17 +++- kernel/kexec.c | 57 ++++++++++++ kernel/sys.c | 31 ++----- 10 files changed, 269 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c index 29a0e039d436..aab76887a842 100644 --- a/arch/powerpc/kernel/machine_kexec.c +++ b/arch/powerpc/kernel/machine_kexec.c @@ -48,7 +48,7 @@ void machine_kexec_cleanup(struct kimage *image) * Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec(struct kimage *image) { if (ppc_md.machine_kexec) ppc_md.machine_kexec(image); diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c index 5c17de51987e..ec1eadce4aaa 100644 --- a/arch/sh/kernel/machine_kexec.c +++ b/arch/sh/kernel/machine_kexec.c @@ -70,7 +70,7 @@ static void kexec_info(struct kimage *image) * Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec(struct kimage *image) { unsigned long page_list; diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e3cba0b45600..7ecb679f0130 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1279,6 +1279,13 @@ config CRASH_DUMP (CONFIG_RELOCATABLE=y). For more details see Documentation/kdump/kdump.txt +config KEXEC_JUMP + bool "kexec jump (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on KEXEC && PM_SLEEP && X86_32 + help + Invoke code in physical address mode via KEXEC + config PHYSICAL_START hex "Physical address where the kernel is loaded" if (EMBEDDED || CRASH_DUMP) default "0x1000000" if X86_NUMAQ diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c index 8864230d55af..2b67609d0a1c 100644 --- a/arch/x86/kernel/machine_kexec_32.c +++ b/arch/x86/kernel/machine_kexec_32.c @@ -22,6 +22,7 @@ #include #include #include +#include #define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) static u32 kexec_pgd[1024] PAGE_ALIGNED; @@ -85,10 +86,12 @@ static void load_segments(void) * reboot code buffer to allow us to avoid allocations * later. * - * Currently nothing. + * Make control page executable. */ int machine_kexec_prepare(struct kimage *image) { + if (nx_enabled) + set_pages_x(image->control_code_page, 1); return 0; } @@ -98,16 +101,24 @@ int machine_kexec_prepare(struct kimage *image) */ void machine_kexec_cleanup(struct kimage *image) { + if (nx_enabled) + set_pages_nx(image->control_code_page, 1); } /* * Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec(struct kimage *image) { unsigned long page_list[PAGES_NR]; void *control_page; + asmlinkage unsigned long + (*relocate_kernel_ptr)(unsigned long indirection_page, + unsigned long control_page, + unsigned long start_address, + unsigned int has_pae, + unsigned int preserve_context); tracer_disable(); @@ -115,10 +126,11 @@ NORET_TYPE void machine_kexec(struct kimage *image) local_irq_disable(); control_page = page_address(image->control_code_page); - memcpy(control_page, relocate_kernel, PAGE_SIZE); + memcpy(control_page, relocate_kernel, PAGE_SIZE/2); + relocate_kernel_ptr = control_page; page_list[PA_CONTROL_PAGE] = __pa(control_page); - page_list[VA_CONTROL_PAGE] = (unsigned long)relocate_kernel; + page_list[VA_CONTROL_PAGE] = (unsigned long)control_page; page_list[PA_PGD] = __pa(kexec_pgd); page_list[VA_PGD] = (unsigned long)kexec_pgd; #ifdef CONFIG_X86_PAE @@ -131,6 +143,7 @@ NORET_TYPE void machine_kexec(struct kimage *image) page_list[VA_PTE_0] = (unsigned long)kexec_pte0; page_list[PA_PTE_1] = __pa(kexec_pte1); page_list[VA_PTE_1] = (unsigned long)kexec_pte1; + page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page) << PAGE_SHIFT); /* The segment registers are funny things, they have both a * visible and an invisible part. Whenever the visible part is @@ -149,8 +162,10 @@ NORET_TYPE void machine_kexec(struct kimage *image) set_idt(phys_to_virt(0),0); /* now call it */ - relocate_kernel((unsigned long)image->head, (unsigned long)page_list, - image->start, cpu_has_pae); + image->start = relocate_kernel_ptr((unsigned long)image->head, + (unsigned long)page_list, + image->start, cpu_has_pae, + image->preserve_context); } void arch_crash_save_vmcoreinfo(void) diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index 9dd9262693a3..c43caa3a91f3 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -181,7 +181,7 @@ void machine_kexec_cleanup(struct kimage *image) * Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -NORET_TYPE void machine_kexec(struct kimage *image) +void machine_kexec(struct kimage *image) { unsigned long page_list[PAGES_NR]; void *control_page; diff --git a/arch/x86/kernel/relocate_kernel_32.S b/arch/x86/kernel/relocate_kernel_32.S index c30fe25d470d..703310a99023 100644 --- a/arch/x86/kernel/relocate_kernel_32.S +++ b/arch/x86/kernel/relocate_kernel_32.S @@ -20,11 +20,44 @@ #define PAGE_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) #define PAE_PGD_ATTR (_PAGE_PRESENT) +/* control_page + PAGE_SIZE/2 ~ control_page + PAGE_SIZE * 3/4 are + * used to save some data for jumping back + */ +#define DATA(offset) (PAGE_SIZE/2+(offset)) + +/* Minimal CPU state */ +#define ESP DATA(0x0) +#define CR0 DATA(0x4) +#define CR3 DATA(0x8) +#define CR4 DATA(0xc) + +/* other data */ +#define CP_VA_CONTROL_PAGE DATA(0x10) +#define CP_PA_PGD DATA(0x14) +#define CP_PA_SWAP_PAGE DATA(0x18) +#define CP_PA_BACKUP_PAGES_MAP DATA(0x1c) + .text .align PAGE_SIZE .globl relocate_kernel relocate_kernel: - movl 8(%esp), %ebp /* list of pages */ + /* Save the CPU context, used for jumping back */ + + pushl %ebx + pushl %esi + pushl %edi + pushl %ebp + pushf + + movl 20+8(%esp), %ebp /* list of pages */ + movl PTR(VA_CONTROL_PAGE)(%ebp), %edi + movl %esp, ESP(%edi) + movl %cr0, %eax + movl %eax, CR0(%edi) + movl %cr3, %eax + movl %eax, CR3(%edi) + movl %cr4, %eax + movl %eax, CR4(%edi) #ifdef CONFIG_X86_PAE /* map the control page at its virtual address */ @@ -138,15 +171,25 @@ relocate_kernel: relocate_new_kernel: /* read the arguments and say goodbye to the stack */ - movl 4(%esp), %ebx /* page_list */ - movl 8(%esp), %ebp /* list of pages */ - movl 12(%esp), %edx /* start address */ - movl 16(%esp), %ecx /* cpu_has_pae */ + movl 20+4(%esp), %ebx /* page_list */ + movl 20+8(%esp), %ebp /* list of pages */ + movl 20+12(%esp), %edx /* start address */ + movl 20+16(%esp), %ecx /* cpu_has_pae */ + movl 20+20(%esp), %esi /* preserve_context */ /* zero out flags, and disable interrupts */ pushl $0 popfl + /* save some information for jumping back */ + movl PTR(VA_CONTROL_PAGE)(%ebp), %edi + movl %edi, CP_VA_CONTROL_PAGE(%edi) + movl PTR(PA_PGD)(%ebp), %eax + movl %eax, CP_PA_PGD(%edi) + movl PTR(PA_SWAP_PAGE)(%ebp), %eax + movl %eax, CP_PA_SWAP_PAGE(%edi) + movl %ebx, CP_PA_BACKUP_PAGES_MAP(%edi) + /* get physical address of control page now */ /* this is impossible after page table switch */ movl PTR(PA_CONTROL_PAGE)(%ebp), %edi @@ -197,8 +240,90 @@ identity_mapped: xorl %eax, %eax movl %eax, %cr3 + movl CP_PA_SWAP_PAGE(%edi), %eax + pushl %eax + pushl %ebx + call swap_pages + addl $8, %esp + + /* To be certain of avoiding problems with self-modifying code + * I need to execute a serializing instruction here. + * So I flush the TLB, it's handy, and not processor dependent. + */ + xorl %eax, %eax + movl %eax, %cr3 + + /* set all of the registers to known values */ + /* leave %esp alone */ + + testl %esi, %esi + jnz 1f + xorl %edi, %edi + xorl %eax, %eax + xorl %ebx, %ebx + xorl %ecx, %ecx + xorl %edx, %edx + xorl %esi, %esi + xorl %ebp, %ebp + ret +1: + popl %edx + movl CP_PA_SWAP_PAGE(%edi), %esp + addl $PAGE_SIZE, %esp +2: + call *%edx + + /* get the re-entry point of the peer system */ + movl 0(%esp), %ebp + call 1f +1: + popl %ebx + subl $(1b - relocate_kernel), %ebx + movl CP_VA_CONTROL_PAGE(%ebx), %edi + lea PAGE_SIZE(%ebx), %esp + movl CP_PA_SWAP_PAGE(%ebx), %eax + movl CP_PA_BACKUP_PAGES_MAP(%ebx), %edx + pushl %eax + pushl %edx + call swap_pages + addl $8, %esp + movl CP_PA_PGD(%ebx), %eax + movl %eax, %cr3 + movl %cr0, %eax + orl $(1<<31), %eax + movl %eax, %cr0 + lea PAGE_SIZE(%edi), %esp + movl %edi, %eax + addl $(virtual_mapped - relocate_kernel), %eax + pushl %eax + ret + +virtual_mapped: + movl CR4(%edi), %eax + movl %eax, %cr4 + movl CR3(%edi), %eax + movl %eax, %cr3 + movl CR0(%edi), %eax + movl %eax, %cr0 + movl ESP(%edi), %esp + movl %ebp, %eax + + popf + popl %ebp + popl %edi + popl %esi + popl %ebx + ret + /* Do the copies */ - movl %ebx, %ecx +swap_pages: + movl 8(%esp), %edx + movl 4(%esp), %ecx + pushl %ebp + pushl %ebx + pushl %edi + pushl %esi + movl %ecx, %ebx jmp 1f 0: /* top, read another word from the indirection page */ @@ -226,27 +351,28 @@ identity_mapped: movl %ecx, %esi /* For every source page do a copy */ andl $0xfffff000, %esi + movl %edi, %eax + movl %esi, %ebp + + movl %edx, %edi movl $1024, %ecx rep ; movsl - jmp 0b -3: - - /* To be certain of avoiding problems with self-modifying code - * I need to execute a serializing instruction here. - * So I flush the TLB, it's handy, and not processor dependent. - */ - xorl %eax, %eax - movl %eax, %cr3 + movl %ebp, %edi + movl %eax, %esi + movl $1024, %ecx + rep ; movsl - /* set all of the registers to known values */ - /* leave %esp alone */ + movl %eax, %edi + movl %edx, %esi + movl $1024, %ecx + rep ; movsl - xorl %eax, %eax - xorl %ebx, %ebx - xorl %ecx, %ecx - xorl %edx, %edx - xorl %esi, %esi - xorl %edi, %edi - xorl %ebp, %ebp + lea PAGE_SIZE(%ebp), %esi + jmp 0b +3: + popl %esi + popl %edi + popl %ebx + popl %ebp ret diff --git a/include/asm-x86/kexec.h b/include/asm-x86/kexec.h index 8f855a15f64d..c0e52a14fd4d 100644 --- a/include/asm-x86/kexec.h +++ b/include/asm-x86/kexec.h @@ -10,14 +10,15 @@ # define VA_PTE_0 5 # define PA_PTE_1 6 # define VA_PTE_1 7 +# define PA_SWAP_PAGE 8 # ifdef CONFIG_X86_PAE -# define PA_PMD_0 8 -# define VA_PMD_0 9 -# define PA_PMD_1 10 -# define VA_PMD_1 11 -# define PAGES_NR 12 +# define PA_PMD_0 9 +# define VA_PMD_0 10 +# define PA_PMD_1 11 +# define VA_PMD_1 12 +# define PAGES_NR 13 # else -# define PAGES_NR 8 +# define PAGES_NR 9 # endif #else # define PA_CONTROL_PAGE 0 @@ -152,11 +153,12 @@ static inline void crash_setup_regs(struct pt_regs *newregs, } #ifdef CONFIG_X86_32 -asmlinkage NORET_TYPE void +asmlinkage unsigned long relocate_kernel(unsigned long indirection_page, unsigned long control_page, unsigned long start_address, - unsigned int has_pae) ATTRIB_NORET; + unsigned int has_pae, + unsigned int preserve_context); #else NORET_TYPE void relocate_kernel(unsigned long indirection_page, diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 3265968cd2cd..82f88a8a827b 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -83,6 +83,7 @@ struct kimage { unsigned long start; struct page *control_code_page; + struct page *swap_page; unsigned long nr_segments; struct kexec_segment segment[KEXEC_SEGMENT_MAX]; @@ -98,18 +99,20 @@ struct kimage { unsigned int type : 1; #define KEXEC_TYPE_DEFAULT 0 #define KEXEC_TYPE_CRASH 1 + unsigned int preserve_context : 1; }; /* kexec interface functions */ -extern NORET_TYPE void machine_kexec(struct kimage *image) ATTRIB_NORET; +extern void machine_kexec(struct kimage *image); extern int machine_kexec_prepare(struct kimage *image); extern void machine_kexec_cleanup(struct kimage *image); extern asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments, struct kexec_segment __user *segments, unsigned long flags); +extern int kernel_kexec(void); #ifdef CONFIG_COMPAT extern asmlinkage long compat_sys_kexec_load(unsigned long entry, unsigned long nr_segments, @@ -156,8 +159,9 @@ extern struct kimage *kexec_crash_image; #define kexec_flush_icache_page(page) #endif -#define KEXEC_ON_CRASH 0x00000001 -#define KEXEC_ARCH_MASK 0xffff0000 +#define KEXEC_ON_CRASH 0x00000001 +#define KEXEC_PRESERVE_CONTEXT 0x00000002 +#define KEXEC_ARCH_MASK 0xffff0000 /* These values match the ELF architecture values. * Unless there is a good reason that should continue to be the case. @@ -174,7 +178,12 @@ extern struct kimage *kexec_crash_image; #define KEXEC_ARCH_MIPS_LE (10 << 16) #define KEXEC_ARCH_MIPS ( 8 << 16) -#define KEXEC_FLAGS (KEXEC_ON_CRASH) /* List of defined/legal kexec flags */ +/* List of defined/legal kexec flags */ +#ifndef CONFIG_KEXEC_JUMP +#define KEXEC_FLAGS KEXEC_ON_CRASH +#else +#define KEXEC_FLAGS (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT) +#endif #define VMCOREINFO_BYTES (4096) #define VMCOREINFO_NOTE_NAME "VMCOREINFO" diff --git a/kernel/kexec.c b/kernel/kexec.c index 6db42ff8d520..a0d920915b38 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -242,6 +244,12 @@ static int kimage_normal_alloc(struct kimage **rimage, unsigned long entry, goto out; } + image->swap_page = kimage_alloc_control_pages(image, 0); + if (!image->swap_page) { + printk(KERN_ERR "Could not allocate swap buffer\n"); + goto out; + } + result = 0; out: if (result == 0) @@ -986,6 +994,8 @@ asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments, if (result) goto out; + if (flags & KEXEC_PRESERVE_CONTEXT) + image->preserve_context = 1; result = machine_kexec_prepare(image); if (result) goto out; @@ -1411,3 +1421,50 @@ static int __init crash_save_vmcoreinfo_init(void) } module_init(crash_save_vmcoreinfo_init) + +/** + * kernel_kexec - reboot the system + * + * Move into place and start executing a preloaded standalone + * executable. If nothing was preloaded return an error. + */ +int kernel_kexec(void) +{ + int error = 0; + + if (xchg(&kexec_lock, 1)) + return -EBUSY; + if (!kexec_image) { + error = -EINVAL; + goto Unlock; + } + + if (kexec_image->preserve_context) { +#ifdef CONFIG_KEXEC_JUMP + local_irq_disable(); + save_processor_state(); +#endif + } else { + blocking_notifier_call_chain(&reboot_notifier_list, + SYS_RESTART, NULL); + system_state = SYSTEM_RESTART; + device_shutdown(); + sysdev_shutdown(); + printk(KERN_EMERG "Starting new kernel\n"); + machine_shutdown(); + } + + machine_kexec(kexec_image); + + if (kexec_image->preserve_context) { +#ifdef CONFIG_KEXEC_JUMP + restore_processor_state(); + local_irq_enable(); +#endif + } + + Unlock: + xchg(&kexec_lock, 0); + + return error; +} diff --git a/kernel/sys.c b/kernel/sys.c index 0c9d3fa1f5ff..c01858090a98 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -301,26 +301,6 @@ void kernel_restart(char *cmd) } EXPORT_SYMBOL_GPL(kernel_restart); -/** - * kernel_kexec - reboot the system - * - * Move into place and start executing a preloaded standalone - * executable. If nothing was preloaded return an error. - */ -static void kernel_kexec(void) -{ -#ifdef CONFIG_KEXEC - struct kimage *image; - image = xchg(&kexec_image, NULL); - if (!image) - return; - kernel_restart_prepare(NULL); - printk(KERN_EMERG "Starting new kernel\n"); - machine_shutdown(); - machine_kexec(image); -#endif -} - static void kernel_shutdown_prepare(enum system_states state) { blocking_notifier_call_chain(&reboot_notifier_list, @@ -425,10 +405,15 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user kernel_restart(buffer); break; +#ifdef CONFIG_KEXEC case LINUX_REBOOT_CMD_KEXEC: - kernel_kexec(); - unlock_kernel(); - return -EINVAL; + { + int ret; + ret = kernel_kexec(); + unlock_kernel(); + return ret; + } +#endif #ifdef CONFIG_HIBERNATION case LINUX_REBOOT_CMD_SW_SUSPEND: -- cgit From 89081d17f7bb81d89fa1aa9b70f821c5cf4d39e9 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Fri, 25 Jul 2008 19:45:10 -0700 Subject: kexec jump: save/restore device state This patch implements devices state save/restore before after kexec. This patch together with features in kexec_jump patch can be used for following: - A simple hibernation implementation without ACPI support. You can kexec a hibernating kernel, save the memory image of original system and shutdown the system. When resuming, you restore the memory image of original system via ordinary kexec load then jump back. - Kernel/system debug through making system snapshot. You can make system snapshot, jump back, do some thing and make another system snapshot. - Cooperative multi-kernel/system. With kexec jump, you can switch between several kernels/systems quickly without boot process except the first time. This appears like swap a whole kernel/system out/in. - A general method to call program in physical mode (paging turning off). This can be used to invoke BIOS code under Linux. The following user-space tools can be used with kexec jump: - kexec-tools needs to be patched to support kexec jump. The patches and the precompiled kexec can be download from the following URL: source: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec-tools-src_git_kh10.tar.bz2 patches: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec-tools-patches_git_kh10.tar.bz2 binary: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec_git_kh10 - makedumpfile with patches are used as memory image saving tool, it can exclude free pages from original kernel memory image file. The patches and the precompiled makedumpfile can be download from the following URL: source: http://khibernation.sourceforge.net/download/release_v10/makedumpfile/makedumpfile-src_cvs_kh10.tar.bz2 patches: http://khibernation.sourceforge.net/download/release_v10/makedumpfile/makedumpfile-patches_cvs_kh10.tar.bz2 binary: http://khibernation.sourceforge.net/download/release_v10/makedumpfile/makedumpfile_cvs_kh10 - An initramfs image can be used as the root file system of kexeced kernel. An initramfs image built with "BuildRoot" can be downloaded from the following URL: initramfs image: http://khibernation.sourceforge.net/download/release_v10/initramfs/rootfs_cvs_kh10.gz All user space tools above are included in the initramfs image. Usage example of simple hibernation: 1. Compile and install patched kernel with following options selected: CONFIG_X86_32=y CONFIG_RELOCATABLE=y CONFIG_KEXEC=y CONFIG_CRASH_DUMP=y CONFIG_PM=y CONFIG_HIBERNATION=y CONFIG_KEXEC_JUMP=y 2. Build an initramfs image contains kexec-tool and makedumpfile, or download the pre-built initramfs image, called rootfs.gz in following text. 3. Prepare a partition to save memory image of original kernel, called hibernating partition in following text. 4. Boot kernel compiled in step 1 (kernel A). 5. In the kernel A, load kernel compiled in step 1 (kernel B) with /sbin/kexec. The shell command line can be as follow: /sbin/kexec --load-preserve-context /boot/bzImage --mem-min=0x100000 --mem-max=0xffffff --initrd=rootfs.gz 6. Boot the kernel B with following shell command line: /sbin/kexec -e 7. The kernel B will boot as normal kexec. In kernel B the memory image of kernel A can be saved into hibernating partition as follow: jump_back_entry=`cat /proc/cmdline | tr ' ' '\n' | grep kexec_jump_back_entry | cut -d '='` echo $jump_back_entry > kexec_jump_back_entry cp /proc/vmcore dump.elf Then you can shutdown the machine as normal. 8. Boot kernel compiled in step 1 (kernel C). Use the rootfs.gz as root file system. 9. In kernel C, load the memory image of kernel A as follow: /sbin/kexec -l --args-none --entry=`cat kexec_jump_back_entry` dump.elf 10. Jump back to the kernel A as follow: /sbin/kexec -e Then, kernel A is resumed. Implementation point: To support jumping between two kernels, before jumping to (executing) the new kernel and jumping back to the original kernel, the devices are put into quiescent state, and the state of devices and CPU is saved. After jumping back from kexeced kernel and jumping to the new kernel, the state of devices and CPU are restored accordingly. The devices/CPU state save/restore code of software suspend is called to implement corresponding function. Known issues: - Because the segment number supported by sys_kexec_load is limited, hibernation image with many segments may not be load. This is planned to be eliminated by adding a new flag to sys_kexec_load to make a image can be loaded with multiple sys_kexec_load invoking. Now, only the i386 architecture is supported. Signed-off-by: Huang Ying Acked-by: Vivek Goyal Cc: "Eric W. Biederman" Cc: Pavel Machek Cc: Nigel Cunningham Cc: "Rafael J. Wysocki" Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/Kconfig | 5 +++-- arch/x86/kernel/machine_kexec_32.c | 12 ++++++++++++ include/linux/suspend.h | 2 ++ kernel/kexec.c | 39 ++++++++++++++++++++++++++++++++++++++ kernel/power/power.h | 2 -- 5 files changed, 56 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 7ecb679f0130..6b2debfabddc 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1282,9 +1282,10 @@ config CRASH_DUMP config KEXEC_JUMP bool "kexec jump (EXPERIMENTAL)" depends on EXPERIMENTAL - depends on KEXEC && PM_SLEEP && X86_32 + depends on KEXEC && HIBERNATION && X86_32 help - Invoke code in physical address mode via KEXEC + Jump between original kernel and kexeced kernel and invoke + code in physical address mode via KEXEC config PHYSICAL_START hex "Physical address where the kernel is loaded" if (EMBEDDED || CRASH_DUMP) diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c index 2b67609d0a1c..9fe478d98406 100644 --- a/arch/x86/kernel/machine_kexec_32.c +++ b/arch/x86/kernel/machine_kexec_32.c @@ -125,6 +125,18 @@ void machine_kexec(struct kimage *image) /* Interrupts aren't acceptable while we reboot */ local_irq_disable(); + if (image->preserve_context) { +#ifdef CONFIG_X86_IO_APIC + /* We need to put APICs in legacy mode so that we can + * get timer interrupts in second kernel. kexec/kdump + * paths already have calls to disable_IO_APIC() in + * one form or other. kexec jump path also need + * one. + */ + disable_IO_APIC(); +#endif + } + control_page = page_address(image->control_code_page); memcpy(control_page, relocate_kernel, PAGE_SIZE/2); diff --git a/include/linux/suspend.h b/include/linux/suspend.h index e8e69159af71..c63435095970 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -278,4 +278,6 @@ static inline void register_nosave_region_late(unsigned long b, unsigned long e) } #endif +extern struct mutex pm_mutex; + #endif /* _LINUX_SUSPEND_H */ diff --git a/kernel/kexec.c b/kernel/kexec.c index a0d920915b38..c8a4370e2a34 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -26,6 +26,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -1441,7 +1445,31 @@ int kernel_kexec(void) if (kexec_image->preserve_context) { #ifdef CONFIG_KEXEC_JUMP + mutex_lock(&pm_mutex); + pm_prepare_console(); + error = freeze_processes(); + if (error) { + error = -EBUSY; + goto Restore_console; + } + suspend_console(); + error = device_suspend(PMSG_FREEZE); + if (error) + goto Resume_console; + error = disable_nonboot_cpus(); + if (error) + goto Resume_devices; local_irq_disable(); + /* At this point, device_suspend() has been called, + * but *not* device_power_down(). We *must* + * device_power_down() now. Otherwise, drivers for + * some devices (e.g. interrupt controllers) become + * desynchronized with the actual state of the + * hardware at resume time, and evil weirdness ensues. + */ + error = device_power_down(PMSG_FREEZE); + if (error) + goto Enable_irqs; save_processor_state(); #endif } else { @@ -1459,7 +1487,18 @@ int kernel_kexec(void) if (kexec_image->preserve_context) { #ifdef CONFIG_KEXEC_JUMP restore_processor_state(); + device_power_up(PMSG_RESTORE); + Enable_irqs: local_irq_enable(); + enable_nonboot_cpus(); + Resume_devices: + device_resume(PMSG_RESTORE); + Resume_console: + resume_console(); + thaw_processes(); + Restore_console: + pm_restore_console(); + mutex_unlock(&pm_mutex); #endif } diff --git a/kernel/power/power.h b/kernel/power/power.h index 700f44ec8406..acc0c101dbd5 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -53,8 +53,6 @@ extern int hibernation_platform_enter(void); extern int pfn_is_nosave(unsigned long); -extern struct mutex pm_mutex; - #define power_attr(_name) \ static struct kobj_attribute _name##_attr = { \ .attr = { \ -- cgit From c2147a5092cfe13dbf3210e54e8a622015edeecc Mon Sep 17 00:00:00 2001 From: Eduard - Gabriel Munteanu Date: Fri, 25 Jul 2008 19:45:11 -0700 Subject: Better interface for hooking early initcalls Added early initcall (pre-SMP) support, using an identical interface to that of regular initcalls. Functions called from do_pre_smp_initcalls() could be converted to use this cleaner interface. This is required by CPU hotplug, because early users have to register notifiers before going SMP. One such CPU hotplug user is the relay interface with buffer-only channels, which needs to register such a notifier, to be usable in early code. This in turn is used by kmemtrace. Signed-off-by: Eduard - Gabriel Munteanu Cc: Tom Zanussi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/vmlinux.lds.h | 2 ++ include/linux/init.h | 7 +++++++ init/main.c | 13 +++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 729f6b0a60e9..9cd44b162ba1 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -359,6 +359,8 @@ } #define INITCALLS \ + *(.initcallearly.init) \ + __early_initcall_end = .; \ *(.initcall0.init) \ *(.initcall0s.init) \ *(.initcall1.init) \ diff --git a/include/linux/init.h b/include/linux/init.h index 42ae95411a93..11b84e106053 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -169,6 +169,13 @@ extern void (*late_time_init)(void); static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn +/* + * Early initcalls run before initializing SMP. + * + * Only for built-in code, not modules. + */ +#define early_initcall(fn) __define_initcall("early",fn,early) + /* * A "pure" initcall has no dependencies on anything else, and purely * initializes variables that couldn't be statically initialized. diff --git a/init/main.c b/init/main.c index 0604cbcaf1e4..b6fec08dbbef 100644 --- a/init/main.c +++ b/init/main.c @@ -743,13 +743,13 @@ static void __init do_one_initcall(initcall_t fn) } -extern initcall_t __initcall_start[], __initcall_end[]; +extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[]; static void __init do_initcalls(void) { initcall_t *call; - for (call = __initcall_start; call < __initcall_end; call++) + for (call = __early_initcall_end; call < __initcall_end; call++) do_one_initcall(*call); /* Make sure there is no pending stuff from the initcall sequence */ @@ -783,6 +783,14 @@ static int __init nosoftlockup_setup(char *str) } __setup("nosoftlockup", nosoftlockup_setup); +static void __init __do_pre_smp_initcalls(void) +{ + initcall_t *call; + + for (call = __initcall_start; call < __early_initcall_end; call++) + do_one_initcall(*call); +} + static void __init do_pre_smp_initcalls(void) { extern int spawn_ksoftirqd(void); @@ -865,6 +873,7 @@ static int __init kernel_init(void * unused) smp_prepare_cpus(setup_max_cpus); + __do_pre_smp_initcalls(); do_pre_smp_initcalls(); smp_init(); -- cgit From 7babe8db99d305340cf4828ce1f5a1481d5622ef Mon Sep 17 00:00:00 2001 From: Eduard - Gabriel Munteanu Date: Fri, 25 Jul 2008 19:45:11 -0700 Subject: Full conversion to early_initcall() interface, remove old interface A previous patch added the early_initcall(), to allow a cleaner hooking of pre-SMP initcalls. Now we remove the older interface, converting all existing users to the new one. [akpm@linux-foundation.org: cleanups] [akpm@linux-foundation.org: build fix] [kosaki.motohiro@jp.fujitsu.com: warning fix] [kosaki.motohiro@jp.fujitsu.com: warning fix] Signed-off-by: Eduard - Gabriel Munteanu Cc: Tom Zanussi Signed-off-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 9 --------- include/linux/smp.h | 5 ----- init/main.c | 23 +---------------------- kernel/sched.c | 5 ++++- kernel/smp.c | 4 +++- kernel/softirq.c | 3 ++- kernel/softlockup.c | 25 ++++++++++++++++++++++--- 7 files changed, 32 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 3260a5c42b91..adb8077dc463 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -292,7 +292,6 @@ extern void sched_show_task(struct task_struct *p); #ifdef CONFIG_DETECT_SOFTLOCKUP extern void softlockup_tick(void); -extern void spawn_softlockup_task(void); extern void touch_softlockup_watchdog(void); extern void touch_all_softlockup_watchdogs(void); extern unsigned int softlockup_panic; @@ -2222,14 +2221,6 @@ static inline void inc_syscw(struct task_struct *tsk) } #endif -#ifdef CONFIG_SMP -void migration_init(void); -#else -static inline void migration_init(void) -{ -} -#endif - #ifndef TASK_SIZE_OF #define TASK_SIZE_OF(tsk) TASK_SIZE #endif diff --git a/include/linux/smp.h b/include/linux/smp.h index 48262f86c969..66484d4a8459 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -74,15 +74,10 @@ void __smp_call_function_single(int cpuid, struct call_single_data *data); #ifdef CONFIG_USE_GENERIC_SMP_HELPERS void generic_smp_call_function_single_interrupt(void); void generic_smp_call_function_interrupt(void); -void init_call_single_data(void); void ipi_call_lock(void); void ipi_call_unlock(void); void ipi_call_lock_irq(void); void ipi_call_unlock_irq(void); -#else -static inline void init_call_single_data(void) -{ -} #endif /* diff --git a/init/main.c b/init/main.c index b6fec08dbbef..20fdc9884b77 100644 --- a/init/main.c +++ b/init/main.c @@ -774,16 +774,7 @@ static void __init do_basic_setup(void) do_initcalls(); } -static int __initdata nosoftlockup; - -static int __init nosoftlockup_setup(char *str) -{ - nosoftlockup = 1; - return 1; -} -__setup("nosoftlockup", nosoftlockup_setup); - -static void __init __do_pre_smp_initcalls(void) +static void __init do_pre_smp_initcalls(void) { initcall_t *call; @@ -791,17 +782,6 @@ static void __init __do_pre_smp_initcalls(void) do_one_initcall(*call); } -static void __init do_pre_smp_initcalls(void) -{ - extern int spawn_ksoftirqd(void); - - init_call_single_data(); - migration_init(); - spawn_ksoftirqd(); - if (!nosoftlockup) - spawn_softlockup_task(); -} - static void run_init_process(char *init_filename) { argv_init[0] = init_filename; @@ -873,7 +853,6 @@ static int __init kernel_init(void * unused) smp_prepare_cpus(setup_max_cpus); - __do_pre_smp_initcalls(); do_pre_smp_initcalls(); smp_init(); diff --git a/kernel/sched.c b/kernel/sched.c index 0047bd9b96aa..fde1a1026359 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -6389,7 +6389,7 @@ static struct notifier_block __cpuinitdata migration_notifier = { .priority = 10 }; -void __init migration_init(void) +static int __init migration_init(void) { void *cpu = (void *)(long)smp_processor_id(); int err; @@ -6399,7 +6399,10 @@ void __init migration_init(void) BUG_ON(err == NOTIFY_BAD); migration_call(&migration_notifier, CPU_ONLINE, cpu); register_cpu_notifier(&migration_notifier); + + return err; } +early_initcall(migration_init); #endif #ifdef CONFIG_SMP diff --git a/kernel/smp.c b/kernel/smp.c index 462c785ca1ee..96fc7c0edc59 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -33,7 +33,7 @@ struct call_single_queue { spinlock_t lock; }; -void __cpuinit init_call_single_data(void) +static int __cpuinit init_call_single_data(void) { int i; @@ -43,7 +43,9 @@ void __cpuinit init_call_single_data(void) spin_lock_init(&q->lock); INIT_LIST_HEAD(&q->list); } + return 0; } +early_initcall(init_call_single_data); static void csd_flag_wait(struct call_single_data *data) { diff --git a/kernel/softirq.c b/kernel/softirq.c index f6b03d56c2bf..c506f266a6b9 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -630,7 +630,7 @@ static struct notifier_block __cpuinitdata cpu_nfb = { .notifier_call = cpu_callback }; -__init int spawn_ksoftirqd(void) +static __init int spawn_ksoftirqd(void) { void *cpu = (void *)(long)smp_processor_id(); int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu); @@ -640,6 +640,7 @@ __init int spawn_ksoftirqd(void) register_cpu_notifier(&cpu_nfb); return 0; } +early_initcall(spawn_ksoftirqd); #ifdef CONFIG_SMP /* diff --git a/kernel/softlockup.c b/kernel/softlockup.c index 7bd8d1aadd5d..b75b492fbfcf 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -338,14 +338,33 @@ static struct notifier_block __cpuinitdata cpu_nfb = { .notifier_call = cpu_callback }; -__init void spawn_softlockup_task(void) +static int __initdata nosoftlockup; + +static int __init nosoftlockup_setup(char *str) +{ + nosoftlockup = 1; + return 1; +} +__setup("nosoftlockup", nosoftlockup_setup); + +static int __init spawn_softlockup_task(void) { void *cpu = (void *)(long)smp_processor_id(); - int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu); + int err; - BUG_ON(err == NOTIFY_BAD); + if (nosoftlockup) + return 0; + + err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu); + if (err == NOTIFY_BAD) { + BUG(); + return 1; + } cpu_callback(&cpu_nfb, CPU_ONLINE, cpu); register_cpu_notifier(&cpu_nfb); atomic_notifier_chain_register(&panic_notifier_list, &panic_block); + + return 0; } +early_initcall(spawn_softlockup_task); -- cgit From 20d8b67c06fa5e74f44e80b0a0fd68c8327f7c6a Mon Sep 17 00:00:00 2001 From: Eduard - Gabriel Munteanu Date: Fri, 25 Jul 2008 19:45:12 -0700 Subject: relay: add buffer-only channels; useful for early logging Allows one to create and use a channel with no associated files. Files can be initialized later. This is useful in scenarios such as logging in early code, before VFS is up. Therefore, such channels can be created and used as soon as kmem_cache_init() completed. This is needed by kmemtrace to do tracing in early kernel code. [kosaki.motohiro@jp.fujitsu.com: build fix] Signed-off-by: Eduard - Gabriel Munteanu Cc: Tom Zanussi Signed-off-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/relay.txt | 10 +++ include/linux/relay.h | 5 ++ kernel/relay.c | 170 ++++++++++++++++++++++++++++++------ 3 files changed, 156 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/relay.txt b/Documentation/filesystems/relay.txt index 094f2d2f38b1..510b722667ac 100644 --- a/Documentation/filesystems/relay.txt +++ b/Documentation/filesystems/relay.txt @@ -294,6 +294,16 @@ user-defined data with a channel, and is immediately available (including in create_buf_file()) via chan->private_data or buf->chan->private_data. +Buffer-only channels +-------------------- + +These channels have no files associated and can be created with +relay_open(NULL, NULL, ...). Such channels are useful in scenarios such +as when doing early tracing in the kernel, before the VFS is up. In these +cases, one may open a buffer-only channel and then call +relay_late_setup_files() when the kernel is ready to handle files, +to expose the buffered data to the userspace. + Channel 'modes' --------------- diff --git a/include/linux/relay.h b/include/linux/relay.h index 6cd8c4425fc7..953fc055e875 100644 --- a/include/linux/relay.h +++ b/include/linux/relay.h @@ -48,6 +48,7 @@ struct rchan_buf size_t *padding; /* padding counts per sub-buffer */ size_t prev_padding; /* temporary variable */ size_t bytes_consumed; /* bytes consumed in cur read subbuf */ + size_t early_bytes; /* bytes consumed before VFS inited */ unsigned int cpu; /* this buf's cpu */ } ____cacheline_aligned; @@ -68,6 +69,7 @@ struct rchan int is_global; /* One global buffer ? */ struct list_head list; /* for channel list */ struct dentry *parent; /* parent dentry passed to open */ + int has_base_filename; /* has a filename associated? */ char base_filename[NAME_MAX]; /* saved base filename */ }; @@ -169,6 +171,9 @@ struct rchan *relay_open(const char *base_filename, size_t n_subbufs, struct rchan_callbacks *cb, void *private_data); +extern int relay_late_setup_files(struct rchan *chan, + const char *base_filename, + struct dentry *parent); extern void relay_close(struct rchan *chan); extern void relay_flush(struct rchan *chan); extern void relay_subbufs_consumed(struct rchan *chan, diff --git a/kernel/relay.c b/kernel/relay.c index 7de644cdec43..04006ef970b8 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -407,6 +407,35 @@ void relay_reset(struct rchan *chan) } EXPORT_SYMBOL_GPL(relay_reset); +static inline void relay_set_buf_dentry(struct rchan_buf *buf, + struct dentry *dentry) +{ + buf->dentry = dentry; + buf->dentry->d_inode->i_size = buf->early_bytes; +} + +static struct dentry *relay_create_buf_file(struct rchan *chan, + struct rchan_buf *buf, + unsigned int cpu) +{ + struct dentry *dentry; + char *tmpname; + + tmpname = kzalloc(NAME_MAX + 1, GFP_KERNEL); + if (!tmpname) + return NULL; + snprintf(tmpname, NAME_MAX, "%s%d", chan->base_filename, cpu); + + /* Create file in fs */ + dentry = chan->cb->create_buf_file(tmpname, chan->parent, + S_IRUSR, buf, + &chan->is_global); + + kfree(tmpname); + + return dentry; +} + /* * relay_open_buf - create a new relay channel buffer * @@ -416,45 +445,34 @@ static struct rchan_buf *relay_open_buf(struct rchan *chan, unsigned int cpu) { struct rchan_buf *buf = NULL; struct dentry *dentry; - char *tmpname; if (chan->is_global) return chan->buf[0]; - tmpname = kzalloc(NAME_MAX + 1, GFP_KERNEL); - if (!tmpname) - goto end; - snprintf(tmpname, NAME_MAX, "%s%d", chan->base_filename, cpu); - buf = relay_create_buf(chan); if (!buf) - goto free_name; + return NULL; + + if (chan->has_base_filename) { + dentry = relay_create_buf_file(chan, buf, cpu); + if (!dentry) + goto free_buf; + relay_set_buf_dentry(buf, dentry); + } buf->cpu = cpu; __relay_reset(buf, 1); - /* Create file in fs */ - dentry = chan->cb->create_buf_file(tmpname, chan->parent, S_IRUSR, - buf, &chan->is_global); - if (!dentry) - goto free_buf; - - buf->dentry = dentry; - if(chan->is_global) { chan->buf[0] = buf; buf->cpu = 0; } - goto free_name; + return buf; free_buf: relay_destroy_buf(buf); - buf = NULL; -free_name: - kfree(tmpname); -end: - return buf; + return NULL; } /** @@ -537,8 +555,8 @@ static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb, /** * relay_open - create a new relay channel - * @base_filename: base name of files to create - * @parent: dentry of parent directory, %NULL for root directory + * @base_filename: base name of files to create, %NULL for buffering only + * @parent: dentry of parent directory, %NULL for root directory or buffer * @subbuf_size: size of sub-buffers * @n_subbufs: number of sub-buffers * @cb: client callback functions @@ -560,8 +578,6 @@ struct rchan *relay_open(const char *base_filename, { unsigned int i; struct rchan *chan; - if (!base_filename) - return NULL; if (!(subbuf_size && n_subbufs)) return NULL; @@ -576,7 +592,10 @@ struct rchan *relay_open(const char *base_filename, chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs); chan->parent = parent; chan->private_data = private_data; - strlcpy(chan->base_filename, base_filename, NAME_MAX); + if (base_filename) { + chan->has_base_filename = 1; + strlcpy(chan->base_filename, base_filename, NAME_MAX); + } setup_callbacks(chan, cb); kref_init(&chan->kref); @@ -604,6 +623,94 @@ free_bufs: } EXPORT_SYMBOL_GPL(relay_open); +struct rchan_percpu_buf_dispatcher { + struct rchan_buf *buf; + struct dentry *dentry; +}; + +/* Called in atomic context. */ +static void __relay_set_buf_dentry(void *info) +{ + struct rchan_percpu_buf_dispatcher *p = info; + + relay_set_buf_dentry(p->buf, p->dentry); +} + +/** + * relay_late_setup_files - triggers file creation + * @chan: channel to operate on + * @base_filename: base name of files to create + * @parent: dentry of parent directory, %NULL for root directory + * + * Returns 0 if successful, non-zero otherwise. + * + * Use to setup files for a previously buffer-only channel. + * Useful to do early tracing in kernel, before VFS is up, for example. + */ +int relay_late_setup_files(struct rchan *chan, + const char *base_filename, + struct dentry *parent) +{ + int err = 0; + unsigned int i, curr_cpu; + unsigned long flags; + struct dentry *dentry; + struct rchan_percpu_buf_dispatcher disp; + + if (!chan || !base_filename) + return -EINVAL; + + strlcpy(chan->base_filename, base_filename, NAME_MAX); + + mutex_lock(&relay_channels_mutex); + /* Is chan already set up? */ + if (unlikely(chan->has_base_filename)) + return -EEXIST; + chan->has_base_filename = 1; + chan->parent = parent; + curr_cpu = get_cpu(); + /* + * The CPU hotplug notifier ran before us and created buffers with + * no files associated. So it's safe to call relay_setup_buf_file() + * on all currently online CPUs. + */ + for_each_online_cpu(i) { + if (unlikely(!chan->buf[i])) { + printk(KERN_ERR "relay_late_setup_files: CPU %u " + "has no buffer, it must have!\n", i); + BUG(); + err = -EINVAL; + break; + } + + dentry = relay_create_buf_file(chan, chan->buf[i], i); + if (unlikely(!dentry)) { + err = -EINVAL; + break; + } + + if (curr_cpu == i) { + local_irq_save(flags); + relay_set_buf_dentry(chan->buf[i], dentry); + local_irq_restore(flags); + } else { + disp.buf = chan->buf[i]; + disp.dentry = dentry; + smp_mb(); + /* relay_channels_mutex must be held, so wait. */ + err = smp_call_function_single(i, + __relay_set_buf_dentry, + &disp, 1); + } + if (unlikely(err)) + break; + } + put_cpu(); + mutex_unlock(&relay_channels_mutex); + + return err; +} + /** * relay_switch_subbuf - switch to a new sub-buffer * @buf: channel buffer @@ -627,8 +734,13 @@ size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length) old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; buf->padding[old_subbuf] = buf->prev_padding; buf->subbufs_produced++; - buf->dentry->d_inode->i_size += buf->chan->subbuf_size - - buf->padding[old_subbuf]; + if (buf->dentry) + buf->dentry->d_inode->i_size += + buf->chan->subbuf_size - + buf->padding[old_subbuf]; + else + buf->early_bytes += buf->chan->subbuf_size - + buf->padding[old_subbuf]; smp_mb(); if (waitqueue_active(&buf->read_wait)) /* @@ -1237,4 +1349,4 @@ static __init int relay_init(void) return 0; } -module_init(relay_init); +early_initcall(relay_init); -- cgit From 080ccd4573607a930367c2128fc709814b2ade5d Mon Sep 17 00:00:00 2001 From: Huang Weiyi Date: Fri, 25 Jul 2008 19:45:13 -0700 Subject: include/linux/aio.h: removed duplicated include Removed duplicated include in include/linux/aio.h Signed-off-by: Huang Weiyi Signed-off-by: Benjamin LaHaise Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/aio.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/aio.h b/include/linux/aio.h index b51ddd28444e..09b276c35227 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -7,7 +7,6 @@ #include #include -#include #define AIO_MAXSEGS 4 #define AIO_KIOGRP_NR_ATOMIC 8 -- cgit From 21cc199baa815d7b3f1ace4be20b9558cbddc00f Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Fri, 25 Jul 2008 19:45:22 -0700 Subject: mm: introduce get_user_pages_fast Introduce a new get_user_pages_fast mm API, which is basically a get_user_pages with a less general API (but still tends to be suited to the common case): - task and mm are always current and current->mm - force is always 0 - pages is always non-NULL - don't pass back vmas This restricted API can be implemented in a much more scalable way on many architectures when the ptes are present, by walking the page tables locklessly (no mmap_sem or page table locks). When the ptes are not populated, get_user_pages_fast() could be slower. This is implemented locklessly on x86, and used in some key direct IO call sites, in later patches, which provides nearly 10% performance improvement on a threaded database workload. Lots of other code could use this too, depending on use cases (eg. grep drivers/). And it might inspire some new and clever ways to use it. [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Nick Piggin Cc: Dave Kleikamp Cc: Andy Whitcroft Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Andi Kleen Cc: Dave Kleikamp Cc: Badari Pulavarty Cc: Zach Brown Cc: Jens Axboe Reviewed-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index d87a5a5fe87d..f3fd70d6029f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -833,6 +833,39 @@ extern int mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, unsigned long start, unsigned long end, unsigned long newflags); +#ifdef CONFIG_HAVE_GET_USER_PAGES_FAST +/* + * get_user_pages_fast provides equivalent functionality to get_user_pages, + * operating on current and current->mm (force=0 and doesn't return any vmas). + * + * get_user_pages_fast may take mmap_sem and page tables, so no assumptions + * can be made about locking. get_user_pages_fast is to be implemented in a + * way that is advantageous (vs get_user_pages()) when the user memory area is + * already faulted in and present in ptes. However if the pages have to be + * faulted in, it may turn out to be slightly slower). + */ +int get_user_pages_fast(unsigned long start, int nr_pages, int write, + struct page **pages); + +#else +/* + * Should probably be moved to asm-generic, and architectures can include it if + * they don't implement their own get_user_pages_fast. + */ +#define get_user_pages_fast(start, nr_pages, write, pages) \ +({ \ + struct mm_struct *mm = current->mm; \ + int ret; \ + \ + down_read(&mm->mmap_sem); \ + ret = get_user_pages(current, mm, start, nr_pages, \ + write, 0, pages, NULL); \ + up_read(&mm->mmap_sem); \ + \ + ret; \ +}) +#endif + /* * A callback you can register to apply pressure to ageable caches. * -- cgit From 47feff2c8eefe85099f87c43d3096855f0085ca0 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Fri, 25 Jul 2008 19:45:29 -0700 Subject: radix-tree: add gang_lookup_slot, gang_lookup_slot_tag Introduce gang_lookup_slot() and gang_lookup_slot_tag() functions, which are used by lockless pagecache. Signed-off-by: Nick Piggin Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Hugh Dickins Cc: "Paul E. McKenney" Reviewed-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/radix-tree.h | 12 ++- lib/radix-tree.c | 178 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 166 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index b8ce2b444bb5..a916c6660dfa 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -99,12 +99,15 @@ do { \ * * The notable exceptions to this rule are the following functions: * radix_tree_lookup + * radix_tree_lookup_slot * radix_tree_tag_get * radix_tree_gang_lookup + * radix_tree_gang_lookup_slot * radix_tree_gang_lookup_tag + * radix_tree_gang_lookup_tag_slot * radix_tree_tagged * - * The first 4 functions are able to be called locklessly, using RCU. The + * The first 7 functions are able to be called locklessly, using RCU. The * caller must ensure calls to these functions are made within rcu_read_lock() * regions. Other readers (lock-free or otherwise) and modifications may be * running concurrently. @@ -159,6 +162,9 @@ void *radix_tree_delete(struct radix_tree_root *, unsigned long); unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items); +unsigned int +radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results, + unsigned long first_index, unsigned int max_items); unsigned long radix_tree_next_hole(struct radix_tree_root *root, unsigned long index, unsigned long max_scan); int radix_tree_preload(gfp_t gfp_mask); @@ -173,6 +179,10 @@ unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag); +unsigned int +radix_tree_gang_lookup_tag_slot(struct radix_tree_root *root, void ***results, + unsigned long first_index, unsigned int max_items, + unsigned int tag); int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag); static inline void radix_tree_preload_end(void) diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 56ec21a7f73d..9c4f1ffa2864 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -359,18 +359,17 @@ EXPORT_SYMBOL(radix_tree_insert); * Returns: the slot corresponding to the position @index in the * radix tree @root. This is useful for update-if-exists operations. * - * This function cannot be called under rcu_read_lock, it must be - * excluded from writers, as must the returned slot for subsequent - * use by radix_tree_deref_slot() and radix_tree_replace slot. - * Caller must hold tree write locked across slot lookup and - * replace. + * This function can be called under rcu_read_lock iff the slot is not + * modified by radix_tree_replace_slot, otherwise it must be called + * exclusive from other writers. Any dereference of the slot must be done + * using radix_tree_deref_slot. */ void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) { unsigned int height, shift; struct radix_tree_node *node, **slot; - node = root->rnode; + node = rcu_dereference(root->rnode); if (node == NULL) return NULL; @@ -390,7 +389,7 @@ void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) do { slot = (struct radix_tree_node **) (node->slots + ((index>>shift) & RADIX_TREE_MAP_MASK)); - node = *slot; + node = rcu_dereference(*slot); if (node == NULL) return NULL; @@ -667,7 +666,7 @@ unsigned long radix_tree_next_hole(struct radix_tree_root *root, EXPORT_SYMBOL(radix_tree_next_hole); static unsigned int -__lookup(struct radix_tree_node *slot, void **results, unsigned long index, +__lookup(struct radix_tree_node *slot, void ***results, unsigned long index, unsigned int max_items, unsigned long *next_index) { unsigned int nr_found = 0; @@ -701,11 +700,9 @@ __lookup(struct radix_tree_node *slot, void **results, unsigned long index, /* Bottom level: grab some items */ for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) { - struct radix_tree_node *node; index++; - node = slot->slots[i]; - if (node) { - results[nr_found++] = rcu_dereference(node); + if (slot->slots[i]) { + results[nr_found++] = &(slot->slots[i]); if (nr_found == max_items) goto out; } @@ -759,13 +756,22 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, ret = 0; while (ret < max_items) { - unsigned int nr_found; + unsigned int nr_found, slots_found, i; unsigned long next_index; /* Index of next search */ if (cur_index > max_index) break; - nr_found = __lookup(node, results + ret, cur_index, + slots_found = __lookup(node, (void ***)results + ret, cur_index, max_items - ret, &next_index); + nr_found = 0; + for (i = 0; i < slots_found; i++) { + struct radix_tree_node *slot; + slot = *(((void ***)results)[ret + i]); + if (!slot) + continue; + results[ret + nr_found] = rcu_dereference(slot); + nr_found++; + } ret += nr_found; if (next_index == 0) break; @@ -776,12 +782,71 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, } EXPORT_SYMBOL(radix_tree_gang_lookup); +/** + * radix_tree_gang_lookup_slot - perform multiple slot lookup on radix tree + * @root: radix tree root + * @results: where the results of the lookup are placed + * @first_index: start the lookup from this key + * @max_items: place up to this many items at *results + * + * Performs an index-ascending scan of the tree for present items. Places + * their slots at *@results and returns the number of items which were + * placed at *@results. + * + * The implementation is naive. + * + * Like radix_tree_gang_lookup as far as RCU and locking goes. Slots must + * be dereferenced with radix_tree_deref_slot, and if using only RCU + * protection, radix_tree_deref_slot may fail requiring a retry. + */ +unsigned int +radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results, + unsigned long first_index, unsigned int max_items) +{ + unsigned long max_index; + struct radix_tree_node *node; + unsigned long cur_index = first_index; + unsigned int ret; + + node = rcu_dereference(root->rnode); + if (!node) + return 0; + + if (!radix_tree_is_indirect_ptr(node)) { + if (first_index > 0) + return 0; + results[0] = (void **)&root->rnode; + return 1; + } + node = radix_tree_indirect_to_ptr(node); + + max_index = radix_tree_maxindex(node->height); + + ret = 0; + while (ret < max_items) { + unsigned int slots_found; + unsigned long next_index; /* Index of next search */ + + if (cur_index > max_index) + break; + slots_found = __lookup(node, results + ret, cur_index, + max_items - ret, &next_index); + ret += slots_found; + if (next_index == 0) + break; + cur_index = next_index; + } + + return ret; +} +EXPORT_SYMBOL(radix_tree_gang_lookup_slot); + /* * FIXME: the two tag_get()s here should use find_next_bit() instead of * open-coding the search. */ static unsigned int -__lookup_tag(struct radix_tree_node *slot, void **results, unsigned long index, +__lookup_tag(struct radix_tree_node *slot, void ***results, unsigned long index, unsigned int max_items, unsigned long *next_index, unsigned int tag) { unsigned int nr_found = 0; @@ -811,11 +876,9 @@ __lookup_tag(struct radix_tree_node *slot, void **results, unsigned long index, unsigned long j = index & RADIX_TREE_MAP_MASK; for ( ; j < RADIX_TREE_MAP_SIZE; j++) { - struct radix_tree_node *node; index++; if (!tag_get(slot, tag, j)) continue; - node = slot->slots[j]; /* * Even though the tag was found set, we need to * recheck that we have a non-NULL node, because @@ -826,9 +889,8 @@ __lookup_tag(struct radix_tree_node *slot, void **results, unsigned long index, * lookup ->slots[x] without a lock (ie. can't * rely on its value remaining the same). */ - if (node) { - node = rcu_dereference(node); - results[nr_found++] = node; + if (slot->slots[j]) { + results[nr_found++] = &(slot->slots[j]); if (nr_found == max_items) goto out; } @@ -887,13 +949,22 @@ radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, ret = 0; while (ret < max_items) { - unsigned int nr_found; + unsigned int nr_found, slots_found, i; unsigned long next_index; /* Index of next search */ if (cur_index > max_index) break; - nr_found = __lookup_tag(node, results + ret, cur_index, - max_items - ret, &next_index, tag); + slots_found = __lookup_tag(node, (void ***)results + ret, + cur_index, max_items - ret, &next_index, tag); + nr_found = 0; + for (i = 0; i < slots_found; i++) { + struct radix_tree_node *slot; + slot = *(((void ***)results)[ret + i]); + if (!slot) + continue; + results[ret + nr_found] = rcu_dereference(slot); + nr_found++; + } ret += nr_found; if (next_index == 0) break; @@ -904,6 +975,67 @@ radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, } EXPORT_SYMBOL(radix_tree_gang_lookup_tag); +/** + * radix_tree_gang_lookup_tag_slot - perform multiple slot lookup on a + * radix tree based on a tag + * @root: radix tree root + * @results: where the results of the lookup are placed + * @first_index: start the lookup from this key + * @max_items: place up to this many items at *results + * @tag: the tag index (< RADIX_TREE_MAX_TAGS) + * + * Performs an index-ascending scan of the tree for present items which + * have the tag indexed by @tag set. Places the slots at *@results and + * returns the number of slots which were placed at *@results. + */ +unsigned int +radix_tree_gang_lookup_tag_slot(struct radix_tree_root *root, void ***results, + unsigned long first_index, unsigned int max_items, + unsigned int tag) +{ + struct radix_tree_node *node; + unsigned long max_index; + unsigned long cur_index = first_index; + unsigned int ret; + + /* check the root's tag bit */ + if (!root_tag_get(root, tag)) + return 0; + + node = rcu_dereference(root->rnode); + if (!node) + return 0; + + if (!radix_tree_is_indirect_ptr(node)) { + if (first_index > 0) + return 0; + results[0] = (void **)&root->rnode; + return 1; + } + node = radix_tree_indirect_to_ptr(node); + + max_index = radix_tree_maxindex(node->height); + + ret = 0; + while (ret < max_items) { + unsigned int slots_found; + unsigned long next_index; /* Index of next search */ + + if (cur_index > max_index) + break; + slots_found = __lookup_tag(node, results + ret, + cur_index, max_items - ret, &next_index, tag); + ret += slots_found; + if (next_index == 0) + break; + cur_index = next_index; + } + + return ret; +} +EXPORT_SYMBOL(radix_tree_gang_lookup_tag_slot); + + /** * radix_tree_shrink - shrink height of a radix tree to minimal * @root radix tree root -- cgit From e286781d5f2e9c846e012a39653a166e9d31777d Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Fri, 25 Jul 2008 19:45:30 -0700 Subject: mm: speculative page references If we can be sure that elevating the page_count on a pagecache page will pin it, we can speculatively run this operation, and subsequently check to see if we hit the right page rather than relying on holding a lock or otherwise pinning a reference to the page. This can be done if get_page/put_page behaves consistently throughout the whole tree (ie. if we "get" the page after it has been used for something else, we must be able to free it with a put_page). Actually, there is a period where the count behaves differently: when the page is free or if it is a constituent page of a compound page. We need an atomic_inc_not_zero operation to ensure we don't try to grab the page in either case. This patch introduces the core locking protocol to the pagecache (ie. adds page_cache_get_speculative, and tweaks some update-side code to make it work). Thanks to Hugh for pointing out an improvement to the algorithm setting page_count to zero when we have control of all references, in order to hold off speculative getters. [kamezawa.hiroyu@jp.fujitsu.com: fix migration_entry_wait()] [hugh@veritas.com: fix add_to_page_cache] [akpm@linux-foundation.org: repair a comment] Signed-off-by: Nick Piggin Cc: Jeff Garzik Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Hugh Dickins Cc: "Paul E. McKenney" Reviewed-by: Peter Zijlstra Signed-off-by: Daisuke Nishimura Signed-off-by: KAMEZAWA Hiroyuki Signed-off-by: KOSAKI Motohiro Signed-off-by: Hugh Dickins Acked-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/cassini.c | 12 ++++++ include/linux/pagemap.h | 111 +++++++++++++++++++++++++++++++++++++++++++++++- mm/filemap.c | 32 ++++++++------ mm/migrate.c | 20 ++++++++- mm/shmem.c | 6 +-- mm/swap_state.c | 17 +++++--- mm/vmscan.c | 74 +++++++++++++++++++++++--------- 7 files changed, 227 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 83768df27806..f1936d51b458 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -576,6 +576,18 @@ static void cas_spare_recover(struct cas *cp, const gfp_t flags) list_for_each_safe(elem, tmp, &list) { cas_page_t *page = list_entry(elem, cas_page_t, list); + /* + * With the lockless pagecache, cassini buffering scheme gets + * slightly less accurate: we might find that a page has an + * elevated reference count here, due to a speculative ref, + * and skip it as in-use. Ideally we would be able to reclaim + * it. However this would be such a rare case, it doesn't + * matter too much as we should pick it up the next time round. + * + * Importantly, if we find that the page has a refcount of 1 + * here (our refcount), then we know it is definitely not inuse + * so we can reuse it. + */ if (page_count(page->buffer) > 1) continue; diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index ee1ec2c7723c..a81d81890422 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -12,6 +12,7 @@ #include #include #include +#include /* for in_interrupt() */ /* * Bits in mapping->flags. The lower __GFP_BITS_SHIFT bits are the page @@ -62,6 +63,98 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) #define page_cache_release(page) put_page(page) void release_pages(struct page **pages, int nr, int cold); +/* + * speculatively take a reference to a page. + * If the page is free (_count == 0), then _count is untouched, and 0 + * is returned. Otherwise, _count is incremented by 1 and 1 is returned. + * + * This function must be called inside the same rcu_read_lock() section as has + * been used to lookup the page in the pagecache radix-tree (or page table): + * this allows allocators to use a synchronize_rcu() to stabilize _count. + * + * Unless an RCU grace period has passed, the count of all pages coming out + * of the allocator must be considered unstable. page_count may return higher + * than expected, and put_page must be able to do the right thing when the + * page has been finished with, no matter what it is subsequently allocated + * for (because put_page is what is used here to drop an invalid speculative + * reference). + * + * This is the interesting part of the lockless pagecache (and lockless + * get_user_pages) locking protocol, where the lookup-side (eg. find_get_page) + * has the following pattern: + * 1. find page in radix tree + * 2. conditionally increment refcount + * 3. check the page is still in pagecache (if no, goto 1) + * + * Remove-side that cares about stability of _count (eg. reclaim) has the + * following (with tree_lock held for write): + * A. atomically check refcount is correct and set it to 0 (atomic_cmpxchg) + * B. remove page from pagecache + * C. free the page + * + * There are 2 critical interleavings that matter: + * - 2 runs before A: in this case, A sees elevated refcount and bails out + * - A runs before 2: in this case, 2 sees zero refcount and retries; + * subsequently, B will complete and 1 will find no page, causing the + * lookup to return NULL. + * + * It is possible that between 1 and 2, the page is removed then the exact same + * page is inserted into the same position in pagecache. That's OK: the + * old find_get_page using tree_lock could equally have run before or after + * such a re-insertion, depending on order that locks are granted. + * + * Lookups racing against pagecache insertion isn't a big problem: either 1 + * will find the page or it will not. Likewise, the old find_get_page could run + * either before the insertion or afterwards, depending on timing. + */ +static inline int page_cache_get_speculative(struct page *page) +{ + VM_BUG_ON(in_interrupt()); + +#if !defined(CONFIG_SMP) && defined(CONFIG_CLASSIC_RCU) +# ifdef CONFIG_PREEMPT + VM_BUG_ON(!in_atomic()); +# endif + /* + * Preempt must be disabled here - we rely on rcu_read_lock doing + * this for us. + * + * Pagecache won't be truncated from interrupt context, so if we have + * found a page in the radix tree here, we have pinned its refcount by + * disabling preempt, and hence no need for the "speculative get" that + * SMP requires. + */ + VM_BUG_ON(page_count(page) == 0); + atomic_inc(&page->_count); + +#else + if (unlikely(!get_page_unless_zero(page))) { + /* + * Either the page has been freed, or will be freed. + * In either case, retry here and the caller should + * do the right thing (see comments above). + */ + return 0; + } +#endif + VM_BUG_ON(PageTail(page)); + + return 1; +} + +static inline int page_freeze_refs(struct page *page, int count) +{ + return likely(atomic_cmpxchg(&page->_count, count, 0) == count); +} + +static inline void page_unfreeze_refs(struct page *page, int count) +{ + VM_BUG_ON(page_count(page) != 0); + VM_BUG_ON(count == 0); + + atomic_set(&page->_count, count); +} + #ifdef CONFIG_NUMA extern struct page *__page_cache_alloc(gfp_t gfp); #else @@ -133,13 +226,29 @@ static inline struct page *read_mapping_page(struct address_space *mapping, return read_cache_page(mapping, index, filler, data); } -int add_to_page_cache(struct page *page, struct address_space *mapping, +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, pgoff_t index, gfp_t gfp_mask); extern void remove_from_page_cache(struct page *page); extern void __remove_from_page_cache(struct page *page); +/* + * Like add_to_page_cache_locked, but used to add newly allocated pages: + * the page is new, so we can just run SetPageLocked() against it. + */ +static inline int add_to_page_cache(struct page *page, + struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) +{ + int error; + + SetPageLocked(page); + error = add_to_page_cache_locked(page, mapping, offset, gfp_mask); + if (unlikely(error)) + ClearPageLocked(page); + return error; +} + /* * Return byte-offset into filesystem object for page. */ diff --git a/mm/filemap.c b/mm/filemap.c index 2d3ec1ffc66e..4e182a9a14c0 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -442,39 +442,43 @@ int filemap_write_and_wait_range(struct address_space *mapping, } /** - * add_to_page_cache - add newly allocated pagecache pages + * add_to_page_cache_locked - add a locked page to the pagecache * @page: page to add * @mapping: the page's address_space * @offset: page index * @gfp_mask: page allocation mode * - * This function is used to add newly allocated pagecache pages; - * the page is new, so we can just run SetPageLocked() against it. - * The other page state flags were set by rmqueue(). - * + * This function is used to add a page to the pagecache. It must be locked. * This function does not add the page to the LRU. The caller must do that. */ -int add_to_page_cache(struct page *page, struct address_space *mapping, +int add_to_page_cache_locked(struct page *page, struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) { - int error = mem_cgroup_cache_charge(page, current->mm, + int error; + + VM_BUG_ON(!PageLocked(page)); + + error = mem_cgroup_cache_charge(page, current->mm, gfp_mask & ~__GFP_HIGHMEM); if (error) goto out; error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); if (error == 0) { + page_cache_get(page); + page->mapping = mapping; + page->index = offset; + write_lock_irq(&mapping->tree_lock); error = radix_tree_insert(&mapping->page_tree, offset, page); - if (!error) { - page_cache_get(page); - SetPageLocked(page); - page->mapping = mapping; - page->index = offset; + if (likely(!error)) { mapping->nrpages++; __inc_zone_page_state(page, NR_FILE_PAGES); - } else + } else { + page->mapping = NULL; mem_cgroup_uncharge_cache_page(page); + page_cache_release(page); + } write_unlock_irq(&mapping->tree_lock); radix_tree_preload_end(); @@ -483,7 +487,7 @@ int add_to_page_cache(struct page *page, struct address_space *mapping, out: return error; } -EXPORT_SYMBOL(add_to_page_cache); +EXPORT_SYMBOL(add_to_page_cache_locked); int add_to_page_cache_lru(struct page *page, struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) diff --git a/mm/migrate.c b/mm/migrate.c index d8c65a65c61d..3ca6392e82cc 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -285,7 +285,15 @@ void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd, page = migration_entry_to_page(entry); - get_page(page); + /* + * Once radix-tree replacement of page migration started, page_count + * *must* be zero. And, we don't want to call wait_on_page_locked() + * against a page without get_page(). + * So, we use get_page_unless_zero(), here. Even failed, page fault + * will occur again. + */ + if (!get_page_unless_zero(page)) + goto out; pte_unmap_unlock(ptep, ptl); wait_on_page_locked(page); put_page(page); @@ -305,6 +313,7 @@ out: static int migrate_page_move_mapping(struct address_space *mapping, struct page *newpage, struct page *page) { + int expected_count; void **pslot; if (!mapping) { @@ -319,12 +328,18 @@ static int migrate_page_move_mapping(struct address_space *mapping, pslot = radix_tree_lookup_slot(&mapping->page_tree, page_index(page)); - if (page_count(page) != 2 + !!PagePrivate(page) || + expected_count = 2 + !!PagePrivate(page); + if (page_count(page) != expected_count || (struct page *)radix_tree_deref_slot(pslot) != page) { write_unlock_irq(&mapping->tree_lock); return -EAGAIN; } + if (!page_freeze_refs(page, expected_count)) { + write_unlock_irq(&mapping->tree_lock); + return -EAGAIN; + } + /* * Now we know that no one else is looking at the page. */ @@ -338,6 +353,7 @@ static int migrate_page_move_mapping(struct address_space *mapping, radix_tree_replace_slot(pslot, newpage); + page_unfreeze_refs(page, expected_count); /* * Drop cache reference from old page. * We know this isn't the last reference. diff --git a/mm/shmem.c b/mm/shmem.c index f92fea94d037..1089092aecaf 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -936,7 +936,7 @@ found: spin_lock(&info->lock); ptr = shmem_swp_entry(info, idx, NULL); if (ptr && ptr->val == entry.val) { - error = add_to_page_cache(page, inode->i_mapping, + error = add_to_page_cache_locked(page, inode->i_mapping, idx, GFP_NOWAIT); /* does mem_cgroup_uncharge_cache_page on error */ } else /* we must compensate for our precharge above */ @@ -1301,8 +1301,8 @@ repeat: SetPageUptodate(filepage); set_page_dirty(filepage); swap_free(swap); - } else if (!(error = add_to_page_cache( - swappage, mapping, idx, GFP_NOWAIT))) { + } else if (!(error = add_to_page_cache_locked(swappage, mapping, + idx, GFP_NOWAIT))) { info->flags |= SHMEM_PAGEIN; shmem_swp_set(info, entry, 0); shmem_swp_unmap(entry); diff --git a/mm/swap_state.c b/mm/swap_state.c index d8aadaf2a0ba..3e3381d6c7ee 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -64,7 +64,7 @@ void show_swap_cache_info(void) } /* - * add_to_swap_cache resembles add_to_page_cache on swapper_space, + * add_to_swap_cache resembles add_to_page_cache_locked on swapper_space, * but sets SwapCache flag and private instead of mapping and index. */ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) @@ -76,19 +76,26 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) BUG_ON(PagePrivate(page)); error = radix_tree_preload(gfp_mask); if (!error) { + page_cache_get(page); + SetPageSwapCache(page); + set_page_private(page, entry.val); + write_lock_irq(&swapper_space.tree_lock); error = radix_tree_insert(&swapper_space.page_tree, entry.val, page); - if (!error) { - page_cache_get(page); - SetPageSwapCache(page); - set_page_private(page, entry.val); + if (likely(!error)) { total_swapcache_pages++; __inc_zone_page_state(page, NR_FILE_PAGES); INC_CACHE_INFO(add_total); } write_unlock_irq(&swapper_space.tree_lock); radix_tree_preload_end(); + + if (unlikely(error)) { + set_page_private(page, 0UL); + ClearPageSwapCache(page); + page_cache_release(page); + } } return error; } diff --git a/mm/vmscan.c b/mm/vmscan.c index 26672c6cd3ce..0075eac1cd04 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -391,12 +391,10 @@ static pageout_t pageout(struct page *page, struct address_space *mapping, } /* - * Attempt to detach a locked page from its ->mapping. If it is dirty or if - * someone else has a ref on the page, abort and return 0. If it was - * successfully detached, return 1. Assumes the caller has a single ref on - * this page. + * Same as remove_mapping, but if the page is removed from the mapping, it + * gets returned with a refcount of 0. */ -int remove_mapping(struct address_space *mapping, struct page *page) +static int __remove_mapping(struct address_space *mapping, struct page *page) { BUG_ON(!PageLocked(page)); BUG_ON(mapping != page_mapping(page)); @@ -427,24 +425,24 @@ int remove_mapping(struct address_space *mapping, struct page *page) * Note that if SetPageDirty is always performed via set_page_dirty, * and thus under tree_lock, then this ordering is not required. */ - if (unlikely(page_count(page) != 2)) + if (!page_freeze_refs(page, 2)) goto cannot_free; - smp_rmb(); - if (unlikely(PageDirty(page))) + /* note: atomic_cmpxchg in page_freeze_refs provides the smp_rmb */ + if (unlikely(PageDirty(page))) { + page_unfreeze_refs(page, 2); goto cannot_free; + } if (PageSwapCache(page)) { swp_entry_t swap = { .val = page_private(page) }; __delete_from_swap_cache(page); write_unlock_irq(&mapping->tree_lock); swap_free(swap); - __put_page(page); /* The pagecache ref */ - return 1; + } else { + __remove_from_page_cache(page); + write_unlock_irq(&mapping->tree_lock); } - __remove_from_page_cache(page); - write_unlock_irq(&mapping->tree_lock); - __put_page(page); return 1; cannot_free: @@ -452,6 +450,26 @@ cannot_free: return 0; } +/* + * Attempt to detach a locked page from its ->mapping. If it is dirty or if + * someone else has a ref on the page, abort and return 0. If it was + * successfully detached, return 1. Assumes the caller has a single ref on + * this page. + */ +int remove_mapping(struct address_space *mapping, struct page *page) +{ + if (__remove_mapping(mapping, page)) { + /* + * Unfreezing the refcount with 1 rather than 2 effectively + * drops the pagecache ref for us without requiring another + * atomic operation. + */ + page_unfreeze_refs(page, 1); + return 1; + } + return 0; +} + /* * shrink_page_list() returns the number of reclaimed pages */ @@ -598,18 +616,34 @@ static unsigned long shrink_page_list(struct list_head *page_list, if (PagePrivate(page)) { if (!try_to_release_page(page, sc->gfp_mask)) goto activate_locked; - if (!mapping && page_count(page) == 1) - goto free_it; + if (!mapping && page_count(page) == 1) { + unlock_page(page); + if (put_page_testzero(page)) + goto free_it; + else { + /* + * rare race with speculative reference. + * the speculative reference will free + * this page shortly, so we may + * increment nr_reclaimed here (and + * leave it off the LRU). + */ + nr_reclaimed++; + continue; + } + } } - if (!mapping || !remove_mapping(mapping, page)) + if (!mapping || !__remove_mapping(mapping, page)) goto keep_locked; -free_it: unlock_page(page); +free_it: nr_reclaimed++; - if (!pagevec_add(&freed_pvec, page)) - __pagevec_release_nonlru(&freed_pvec); + if (!pagevec_add(&freed_pvec, page)) { + __pagevec_free(&freed_pvec); + pagevec_reinit(&freed_pvec); + } continue; activate_locked: @@ -623,7 +657,7 @@ keep: } list_splice(&ret_pages, page_list); if (pagevec_count(&freed_pvec)) - __pagevec_release_nonlru(&freed_pvec); + __pagevec_free(&freed_pvec); count_vm_events(PGACTIVATE, pgactivate); return nr_reclaimed; } -- cgit From 19fd6231279be3c3bdd02ed99f9b0eb195978064 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Fri, 25 Jul 2008 19:45:32 -0700 Subject: mm: spinlock tree_lock mapping->tree_lock has no read lockers. convert the lock from an rwlock to a spinlock. Signed-off-by: Nick Piggin Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Hugh Dickins Cc: "Paul E. McKenney" Reviewed-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/buffer.c | 4 ++-- fs/inode.c | 2 +- include/asm-arm/cacheflush.h | 4 ++-- include/asm-parisc/cacheflush.h | 4 ++-- include/linux/fs.h | 2 +- mm/filemap.c | 10 +++++----- mm/migrate.c | 11 +++++------ mm/page-writeback.c | 12 ++++++------ mm/swap_state.c | 10 +++++----- mm/swapfile.c | 4 ++-- mm/truncate.c | 6 +++--- mm/vmscan.c | 8 ++++---- 12 files changed, 38 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/fs/buffer.c b/fs/buffer.c index d48caee12e2a..109b261192d9 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -706,7 +706,7 @@ static int __set_page_dirty(struct page *page, if (TestSetPageDirty(page)) return 0; - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); if (page->mapping) { /* Race with truncate? */ WARN_ON_ONCE(warn && !PageUptodate(page)); @@ -719,7 +719,7 @@ static int __set_page_dirty(struct page *page, radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); } - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); return 1; diff --git a/fs/inode.c b/fs/inode.c index c36d9480335c..35b6414522ea 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -209,7 +209,7 @@ void inode_init_once(struct inode *inode) INIT_LIST_HEAD(&inode->i_dentry); INIT_LIST_HEAD(&inode->i_devices); INIT_RADIX_TREE(&inode->i_data.page_tree, GFP_ATOMIC); - rwlock_init(&inode->i_data.tree_lock); + spin_lock_init(&inode->i_data.tree_lock); spin_lock_init(&inode->i_data.i_mmap_lock); INIT_LIST_HEAD(&inode->i_data.private_list); spin_lock_init(&inode->i_data.private_lock); diff --git a/include/asm-arm/cacheflush.h b/include/asm-arm/cacheflush.h index 70b0fe724b62..03cf1ee977b7 100644 --- a/include/asm-arm/cacheflush.h +++ b/include/asm-arm/cacheflush.h @@ -424,9 +424,9 @@ static inline void flush_anon_page(struct vm_area_struct *vma, } #define flush_dcache_mmap_lock(mapping) \ - write_lock_irq(&(mapping)->tree_lock) + spin_lock_irq(&(mapping)->tree_lock) #define flush_dcache_mmap_unlock(mapping) \ - write_unlock_irq(&(mapping)->tree_lock) + spin_unlock_irq(&(mapping)->tree_lock) #define flush_icache_user_range(vma,page,addr,len) \ flush_dcache_page(page) diff --git a/include/asm-parisc/cacheflush.h b/include/asm-parisc/cacheflush.h index 2f1e1b05440a..b7ca6dc7fddc 100644 --- a/include/asm-parisc/cacheflush.h +++ b/include/asm-parisc/cacheflush.h @@ -45,9 +45,9 @@ void flush_cache_mm(struct mm_struct *mm); extern void flush_dcache_page(struct page *page); #define flush_dcache_mmap_lock(mapping) \ - write_lock_irq(&(mapping)->tree_lock) + spin_lock_irq(&(mapping)->tree_lock) #define flush_dcache_mmap_unlock(mapping) \ - write_unlock_irq(&(mapping)->tree_lock) + spin_unlock_irq(&(mapping)->tree_lock) #define flush_icache_page(vma,page) do { \ flush_kernel_dcache_page(page); \ diff --git a/include/linux/fs.h b/include/linux/fs.h index 49d8eb7a71be..53d2edb709b3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -499,7 +499,7 @@ struct backing_dev_info; struct address_space { struct inode *host; /* owner: inode, block_device */ struct radix_tree_root page_tree; /* radix tree of all pages */ - rwlock_t tree_lock; /* and rwlock protecting it */ + spinlock_t tree_lock; /* and lock protecting it */ unsigned int i_mmap_writable;/* count VM_SHARED mappings */ struct prio_tree_root i_mmap; /* tree of private and shared mappings */ struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */ diff --git a/mm/filemap.c b/mm/filemap.c index feb8448d8618..2ed8b0389c51 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -109,7 +109,7 @@ /* * Remove a page from the page cache and free it. Caller has to make * sure the page is locked and that nobody else uses it - or that usage - * is safe. The caller must hold a write_lock on the mapping's tree_lock. + * is safe. The caller must hold the mapping's tree_lock. */ void __remove_from_page_cache(struct page *page) { @@ -141,9 +141,9 @@ void remove_from_page_cache(struct page *page) BUG_ON(!PageLocked(page)); - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); __remove_from_page_cache(page); - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); } static int sync_page(void *word) @@ -469,7 +469,7 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping, page->mapping = mapping; page->index = offset; - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); error = radix_tree_insert(&mapping->page_tree, offset, page); if (likely(!error)) { mapping->nrpages++; @@ -480,7 +480,7 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping, page_cache_release(page); } - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); radix_tree_preload_end(); } else mem_cgroup_uncharge_cache_page(page); diff --git a/mm/migrate.c b/mm/migrate.c index 3ca6392e82cc..153572fb60b8 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -323,7 +323,7 @@ static int migrate_page_move_mapping(struct address_space *mapping, return 0; } - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); pslot = radix_tree_lookup_slot(&mapping->page_tree, page_index(page)); @@ -331,12 +331,12 @@ static int migrate_page_move_mapping(struct address_space *mapping, expected_count = 2 + !!PagePrivate(page); if (page_count(page) != expected_count || (struct page *)radix_tree_deref_slot(pslot) != page) { - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } if (!page_freeze_refs(page, expected_count)) { - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); return -EAGAIN; } @@ -373,10 +373,9 @@ static int migrate_page_move_mapping(struct address_space *mapping, __dec_zone_page_state(page, NR_FILE_PAGES); __inc_zone_page_state(newpage, NR_FILE_PAGES); - write_unlock_irq(&mapping->tree_lock); - if (!PageSwapCache(newpage)) { + spin_unlock_irq(&mapping->tree_lock); + if (!PageSwapCache(newpage)) mem_cgroup_uncharge_cache_page(page); - } return 0; } diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 94c6d8988ab3..24de8b65fdbd 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1088,7 +1088,7 @@ int __set_page_dirty_nobuffers(struct page *page) if (!mapping) return 1; - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); mapping2 = page_mapping(page); if (mapping2) { /* Race with truncate? */ BUG_ON(mapping2 != mapping); @@ -1102,7 +1102,7 @@ int __set_page_dirty_nobuffers(struct page *page) radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); } - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); if (mapping->host) { /* !PageAnon && !swapper_space */ __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); @@ -1258,7 +1258,7 @@ int test_clear_page_writeback(struct page *page) struct backing_dev_info *bdi = mapping->backing_dev_info; unsigned long flags; - write_lock_irqsave(&mapping->tree_lock, flags); + spin_lock_irqsave(&mapping->tree_lock, flags); ret = TestClearPageWriteback(page); if (ret) { radix_tree_tag_clear(&mapping->page_tree, @@ -1269,7 +1269,7 @@ int test_clear_page_writeback(struct page *page) __bdi_writeout_inc(bdi); } } - write_unlock_irqrestore(&mapping->tree_lock, flags); + spin_unlock_irqrestore(&mapping->tree_lock, flags); } else { ret = TestClearPageWriteback(page); } @@ -1287,7 +1287,7 @@ int test_set_page_writeback(struct page *page) struct backing_dev_info *bdi = mapping->backing_dev_info; unsigned long flags; - write_lock_irqsave(&mapping->tree_lock, flags); + spin_lock_irqsave(&mapping->tree_lock, flags); ret = TestSetPageWriteback(page); if (!ret) { radix_tree_tag_set(&mapping->page_tree, @@ -1300,7 +1300,7 @@ int test_set_page_writeback(struct page *page) radix_tree_tag_clear(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); - write_unlock_irqrestore(&mapping->tree_lock, flags); + spin_unlock_irqrestore(&mapping->tree_lock, flags); } else { ret = TestSetPageWriteback(page); } diff --git a/mm/swap_state.c b/mm/swap_state.c index 3e3381d6c7ee..2c217e33d497 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -39,7 +39,7 @@ static struct backing_dev_info swap_backing_dev_info = { struct address_space swapper_space = { .page_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN), - .tree_lock = __RW_LOCK_UNLOCKED(swapper_space.tree_lock), + .tree_lock = __SPIN_LOCK_UNLOCKED(swapper_space.tree_lock), .a_ops = &swap_aops, .i_mmap_nonlinear = LIST_HEAD_INIT(swapper_space.i_mmap_nonlinear), .backing_dev_info = &swap_backing_dev_info, @@ -80,7 +80,7 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) SetPageSwapCache(page); set_page_private(page, entry.val); - write_lock_irq(&swapper_space.tree_lock); + spin_lock_irq(&swapper_space.tree_lock); error = radix_tree_insert(&swapper_space.page_tree, entry.val, page); if (likely(!error)) { @@ -88,7 +88,7 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) __inc_zone_page_state(page, NR_FILE_PAGES); INC_CACHE_INFO(add_total); } - write_unlock_irq(&swapper_space.tree_lock); + spin_unlock_irq(&swapper_space.tree_lock); radix_tree_preload_end(); if (unlikely(error)) { @@ -182,9 +182,9 @@ void delete_from_swap_cache(struct page *page) entry.val = page_private(page); - write_lock_irq(&swapper_space.tree_lock); + spin_lock_irq(&swapper_space.tree_lock); __delete_from_swap_cache(page); - write_unlock_irq(&swapper_space.tree_lock); + spin_unlock_irq(&swapper_space.tree_lock); swap_free(entry); page_cache_release(page); diff --git a/mm/swapfile.c b/mm/swapfile.c index 2f33edb8bee9..af283933c14e 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -369,13 +369,13 @@ int remove_exclusive_swap_page(struct page *page) retval = 0; if (p->swap_map[swp_offset(entry)] == 1) { /* Recheck the page count with the swapcache lock held.. */ - write_lock_irq(&swapper_space.tree_lock); + spin_lock_irq(&swapper_space.tree_lock); if ((page_count(page) == 2) && !PageWriteback(page)) { __delete_from_swap_cache(page); SetPageDirty(page); retval = 1; } - write_unlock_irq(&swapper_space.tree_lock); + spin_unlock_irq(&swapper_space.tree_lock); } spin_unlock(&swap_lock); diff --git a/mm/truncate.c b/mm/truncate.c index b8961cb63414..e68443d74567 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -349,18 +349,18 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page) if (PagePrivate(page) && !try_to_release_page(page, GFP_KERNEL)) return 0; - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); if (PageDirty(page)) goto failed; BUG_ON(PagePrivate(page)); __remove_from_page_cache(page); - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); ClearPageUptodate(page); page_cache_release(page); /* pagecache ref */ return 1; failed: - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); return 0; } diff --git a/mm/vmscan.c b/mm/vmscan.c index 0075eac1cd04..8f71761bc4b7 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -399,7 +399,7 @@ static int __remove_mapping(struct address_space *mapping, struct page *page) BUG_ON(!PageLocked(page)); BUG_ON(mapping != page_mapping(page)); - write_lock_irq(&mapping->tree_lock); + spin_lock_irq(&mapping->tree_lock); /* * The non racy check for a busy page. * @@ -436,17 +436,17 @@ static int __remove_mapping(struct address_space *mapping, struct page *page) if (PageSwapCache(page)) { swp_entry_t swap = { .val = page_private(page) }; __delete_from_swap_cache(page); - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); swap_free(swap); } else { __remove_from_page_cache(page); - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); } return 1; cannot_free: - write_unlock_irq(&mapping->tree_lock); + spin_unlock_irq(&mapping->tree_lock); return 0; } -- cgit From 51cc50685a4275c6a02653670af9f108a64e01cf Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 25 Jul 2008 19:45:34 -0700 Subject: SL*B: drop kmem cache argument from constructor Kmem cache passed to constructor is only needed for constructors that are themselves multiplexeres. Nobody uses this "feature", nor does anybody uses passed kmem cache in non-trivial way, so pass only pointer to object. Non-trivial places are: arch/powerpc/mm/init_64.c arch/powerpc/mm/hugetlbpage.c This is flag day, yes. Signed-off-by: Alexey Dobriyan Acked-by: Pekka Enberg Acked-by: Christoph Lameter Cc: Jon Tollefson Cc: Nick Piggin Cc: Matt Mackall [akpm@linux-foundation.org: fix arch/powerpc/mm/hugetlbpage.c] [akpm@linux-foundation.org: fix mm/slab.c] [akpm@linux-foundation.org: fix ubifs] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/plat-s3c24xx/dma.c | 2 +- arch/powerpc/kernel/rtas_flash.c | 2 +- arch/powerpc/mm/hugetlbpage.c | 9 ++------- arch/powerpc/mm/init_64.c | 24 +++++++++--------------- arch/powerpc/platforms/cell/spufs/inode.c | 2 +- arch/sh/mm/pmb.c | 2 +- arch/xtensa/mm/init.c | 2 +- drivers/usb/mon/mon_text.c | 4 ++-- fs/adfs/super.c | 2 +- fs/affs/super.c | 2 +- fs/afs/super.c | 4 ++-- fs/befs/linuxvfs.c | 2 +- fs/bfs/inode.c | 2 +- fs/block_dev.c | 2 +- fs/buffer.c | 2 +- fs/cifs/cifsfs.c | 2 +- fs/coda/inode.c | 2 +- fs/ecryptfs/main.c | 4 ++-- fs/efs/super.c | 2 +- fs/ext2/super.c | 2 +- fs/ext3/super.c | 2 +- fs/ext4/super.c | 2 +- fs/fat/cache.c | 2 +- fs/fat/inode.c | 2 +- fs/fuse/inode.c | 2 +- fs/gfs2/main.c | 4 ++-- fs/hfs/super.c | 2 +- fs/hfsplus/super.c | 2 +- fs/hpfs/super.c | 2 +- fs/hugetlbfs/inode.c | 2 +- fs/inode.c | 2 +- fs/isofs/inode.c | 2 +- fs/jffs2/super.c | 2 +- fs/jfs/jfs_metapage.c | 2 +- fs/jfs/super.c | 2 +- fs/locks.c | 2 +- fs/minix/inode.c | 2 +- fs/ncpfs/inode.c | 2 +- fs/nfs/inode.c | 2 +- fs/ntfs/super.c | 2 +- fs/ocfs2/dlm/dlmfs.c | 3 +-- fs/ocfs2/super.c | 2 +- fs/openpromfs/inode.c | 2 +- fs/proc/inode.c | 2 +- fs/qnx4/inode.c | 2 +- fs/reiserfs/super.c | 2 +- fs/romfs/inode.c | 2 +- fs/smbfs/inode.c | 2 +- fs/sysv/inode.c | 2 +- fs/ubifs/super.c | 2 +- fs/udf/super.c | 2 +- fs/ufs/super.c | 2 +- fs/xfs/linux-2.6/kmem.h | 2 +- fs/xfs/linux-2.6/xfs_super.c | 1 - include/linux/slab.h | 2 +- include/linux/slub_def.h | 2 +- ipc/mqueue.c | 2 +- kernel/fork.c | 2 +- lib/idr.c | 2 +- lib/radix-tree.c | 2 +- mm/rmap.c | 2 +- mm/shmem.c | 2 +- mm/slab.c | 11 +++++------ mm/slob.c | 7 +++---- mm/slub.c | 13 ++++++------- net/socket.c | 2 +- net/sunrpc/rpc_pipe.c | 2 +- 67 files changed, 90 insertions(+), 106 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c index 60f162dc4fad..8c5e656d5d8c 100644 --- a/arch/arm/plat-s3c24xx/dma.c +++ b/arch/arm/plat-s3c24xx/dma.c @@ -1304,7 +1304,7 @@ struct sysdev_class dma_sysclass = { /* kmem cache implementation */ -static void s3c2410_dma_cache_ctor(struct kmem_cache *c, void *p) +static void s3c2410_dma_cache_ctor(void *p) { memset(p, 0, sizeof(struct s3c2410_dma_buf)); } diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index 09ded5c424a9..149cb112cd1a 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c @@ -286,7 +286,7 @@ static ssize_t rtas_flash_read(struct file *file, char __user *buf, } /* constructor for flash_block_cache */ -void rtas_block_ctor(struct kmem_cache *cache, void *ptr) +void rtas_block_ctor(void *ptr) { memset(ptr, 0, RTAS_BLK_SIZE); } diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index fb42c4dd3217..ed0aab0208a6 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -113,7 +113,7 @@ static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr, static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp, unsigned long address, unsigned int psize) { - pte_t *new = kmem_cache_alloc(huge_pgtable_cache(psize), + pte_t *new = kmem_cache_zalloc(huge_pgtable_cache(psize), GFP_KERNEL|__GFP_REPEAT); if (! new) @@ -730,11 +730,6 @@ static int __init hugepage_setup_sz(char *str) } __setup("hugepagesz=", hugepage_setup_sz); -static void zero_ctor(struct kmem_cache *cache, void *addr) -{ - memset(addr, 0, kmem_cache_size(cache)); -} - static int __init hugetlbpage_init(void) { unsigned int psize; @@ -756,7 +751,7 @@ static int __init hugetlbpage_init(void) HUGEPTE_TABLE_SIZE(psize), HUGEPTE_TABLE_SIZE(psize), 0, - zero_ctor); + NULL); if (!huge_pgtable_cache(psize)) panic("hugetlbpage_init(): could not create %s"\ "\n", HUGEPTE_CACHE_NAME(psize)); diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index a41bc5aa2043..4f7df85129d8 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -136,9 +136,14 @@ static int __init setup_kcore(void) module_init(setup_kcore); #endif -static void zero_ctor(struct kmem_cache *cache, void *addr) +static void pgd_ctor(void *addr) { - memset(addr, 0, kmem_cache_size(cache)); + memset(addr, 0, PGD_TABLE_SIZE); +} + +static void pmd_ctor(void *addr) +{ + memset(addr, 0, PMD_TABLE_SIZE); } static const unsigned int pgtable_cache_size[2] = { @@ -163,19 +168,8 @@ struct kmem_cache *pgtable_cache[ARRAY_SIZE(pgtable_cache_size)]; void pgtable_cache_init(void) { - int i; - - for (i = 0; i < ARRAY_SIZE(pgtable_cache_size); i++) { - int size = pgtable_cache_size[i]; - const char *name = pgtable_cache_name[i]; - - pr_debug("Allocating page table cache %s (#%d) " - "for size: %08x...\n", name, i, size); - pgtable_cache[i] = kmem_cache_create(name, - size, size, - SLAB_PANIC, - zero_ctor); - } + pgtable_cache[0] = kmem_cache_create(pgtable_cache_name[0], PGD_TABLE_SIZE, PGD_TABLE_SIZE, SLAB_PANIC, pgd_ctor); + pgtable_cache[1] = kmem_cache_create(pgtable_cache_name[1], PMD_TABLE_SIZE, PMD_TABLE_SIZE, SLAB_PANIC, pmd_ctor); } #ifdef CONFIG_SPARSEMEM_VMEMMAP diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 7123472801d9..690ca7b0dcf6 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -78,7 +78,7 @@ spufs_destroy_inode(struct inode *inode) } static void -spufs_init_once(struct kmem_cache *cachep, void *p) +spufs_init_once(void *p) { struct spufs_inode_info *ei = p; diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 0b0ec6e04753..46911bcbf17b 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c @@ -293,7 +293,7 @@ void pmb_unmap(unsigned long addr) } while (pmbe); } -static void pmb_cache_ctor(struct kmem_cache *cachep, void *pmb) +static void pmb_cache_ctor(void *pmb) { struct pmb_entry *pmbe = pmb; diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c index 81d0560eaea2..ee261005b363 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c @@ -309,7 +309,7 @@ void show_mem(void) struct kmem_cache *pgtable_cache __read_mostly; -static void pgd_ctor(struct kmem_cache *cache, void* addr) +static void pgd_ctor(void* addr) { pte_t* ptep = (pte_t*)addr; int i; diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 5e3e4e9b6c77..1f715436d6d3 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -87,7 +87,7 @@ struct mon_reader_text { static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */ -static void mon_text_ctor(struct kmem_cache *, void *); +static void mon_text_ctor(void *); struct mon_text_ptr { int cnt, limit; @@ -720,7 +720,7 @@ void mon_text_del(struct mon_bus *mbus) /* * Slab interface: constructor. */ -static void mon_text_ctor(struct kmem_cache *slab, void *mem) +static void mon_text_ctor(void *mem) { /* * Nothing to initialize. No, really! diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 9e421eeb672b..26f3b43726bb 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -249,7 +249,7 @@ static void adfs_destroy_inode(struct inode *inode) kmem_cache_free(adfs_inode_cachep, ADFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct adfs_inode_info *ei = (struct adfs_inode_info *) foo; diff --git a/fs/affs/super.c b/fs/affs/super.c index 4e0309566406..3a89094f93d0 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -90,7 +90,7 @@ static void affs_destroy_inode(struct inode *inode) kmem_cache_free(affs_inode_cachep, AFFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct affs_inode_info *ei = (struct affs_inode_info *) foo; diff --git a/fs/afs/super.c b/fs/afs/super.c index 7e3faeef6818..250d8c4d66e4 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -27,7 +27,7 @@ #define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */ -static void afs_i_init_once(struct kmem_cache *cachep, void *foo); +static void afs_i_init_once(void *foo); static int afs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt); @@ -449,7 +449,7 @@ static void afs_put_super(struct super_block *sb) /* * initialise an inode cache slab element prior to any use */ -static void afs_i_init_once(struct kmem_cache *cachep, void *_vnode) +static void afs_i_init_once(void *_vnode) { struct afs_vnode *vnode = _vnode; diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index e8717de3bab3..02c6e62b72f8 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -289,7 +289,7 @@ befs_destroy_inode(struct inode *inode) kmem_cache_free(befs_inode_cachep, BEFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct befs_inode_info *bi = (struct befs_inode_info *) foo; diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 053e690ec9ed..0ed57b5ee012 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -264,7 +264,7 @@ static void bfs_destroy_inode(struct inode *inode) kmem_cache_free(bfs_inode_cachep, BFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct bfs_inode_info *bi = foo; diff --git a/fs/block_dev.c b/fs/block_dev.c index 10d8a0aa871a..dcf37cada369 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -271,7 +271,7 @@ static void bdev_destroy_inode(struct inode *inode) kmem_cache_free(bdev_cachep, bdi); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct bdev_inode *ei = (struct bdev_inode *) foo; struct block_device *bdev = &ei->bdev; diff --git a/fs/buffer.c b/fs/buffer.c index 109b261192d9..5fd497cdd6f3 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -3272,7 +3272,7 @@ int bh_submit_read(struct buffer_head *bh) EXPORT_SYMBOL(bh_submit_read); static void -init_buffer_head(struct kmem_cache *cachep, void *data) +init_buffer_head(void *data) { struct buffer_head *bh = data; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 22857c639df5..fe5f6809cba6 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -766,7 +766,7 @@ const struct file_operations cifs_dir_ops = { }; static void -cifs_init_once(struct kmem_cache *cachep, void *inode) +cifs_init_once(void *inode) { struct cifsInodeInfo *cifsi = inode; diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 2f58dfc70083..830f51abb971 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -58,7 +58,7 @@ static void coda_destroy_inode(struct inode *inode) kmem_cache_free(coda_inode_cachep, ITOC(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct coda_inode_info *ei = (struct coda_inode_info *) foo; diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 6f403cfba14f..448dfd597b5f 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -578,7 +578,7 @@ static struct file_system_type ecryptfs_fs_type = { * Initializes the ecryptfs_inode_info_cache when it is created */ static void -inode_info_init_once(struct kmem_cache *cachep, void *vptr) +inode_info_init_once(void *vptr) { struct ecryptfs_inode_info *ei = (struct ecryptfs_inode_info *)vptr; @@ -589,7 +589,7 @@ static struct ecryptfs_cache_info { struct kmem_cache **cache; const char *name; size_t size; - void (*ctor)(struct kmem_cache *cache, void *obj); + void (*ctor)(void *obj); } ecryptfs_cache_infos[] = { { .cache = &ecryptfs_auth_tok_list_item_cache, diff --git a/fs/efs/super.c b/fs/efs/super.c index d733531b55e2..567b134fa1f1 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -70,7 +70,7 @@ static void efs_destroy_inode(struct inode *inode) kmem_cache_free(efs_inode_cachep, INODE_INFO(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct efs_inode_info *ei = (struct efs_inode_info *) foo; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 31308a3b0b8b..fd88c7b43e66 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -159,7 +159,7 @@ static void ext2_destroy_inode(struct inode *inode) kmem_cache_free(ext2_inode_cachep, EXT2_I(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct ext2_inode_info *ei = (struct ext2_inode_info *) foo; diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 615788c6843a..8ddced384674 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -472,7 +472,7 @@ static void ext3_destroy_inode(struct inode *inode) kmem_cache_free(ext3_inode_cachep, EXT3_I(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct ext3_inode_info *ei = (struct ext3_inode_info *) foo; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 1cb371dcd609..b5479b1dff14 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -595,7 +595,7 @@ static void ext4_destroy_inode(struct inode *inode) kmem_cache_free(ext4_inode_cachep, EXT4_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct ext4_inode_info *ei = (struct ext4_inode_info *) foo; diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 3a9ecac8d61f..3222f51c41cf 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -36,7 +36,7 @@ static inline int fat_max_cache(struct inode *inode) static struct kmem_cache *fat_cache_cachep; -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct fat_cache *cache = (struct fat_cache *)foo; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 23676f9d79ce..6d266d793e2c 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -498,7 +498,7 @@ static void fat_destroy_inode(struct inode *inode) kmem_cache_free(fat_inode_cachep, MSDOS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct msdos_inode_info *ei = (struct msdos_inode_info *)foo; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 7d2f7d6e22e2..d2249f174e20 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -956,7 +956,7 @@ static inline void unregister_fuseblk(void) } #endif -static void fuse_inode_init_once(struct kmem_cache *cachep, void *foo) +static void fuse_inode_init_once(void *foo) { struct inode * inode = foo; diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index bcc668d0fadd..bb2cc303ac29 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -24,7 +24,7 @@ #include "util.h" #include "glock.h" -static void gfs2_init_inode_once(struct kmem_cache *cachep, void *foo) +static void gfs2_init_inode_once(void *foo) { struct gfs2_inode *ip = foo; @@ -33,7 +33,7 @@ static void gfs2_init_inode_once(struct kmem_cache *cachep, void *foo) ip->i_alloc = NULL; } -static void gfs2_init_glock_once(struct kmem_cache *cachep, void *foo) +static void gfs2_init_glock_once(void *foo) { struct gfs2_glock *gl = foo; diff --git a/fs/hfs/super.c b/fs/hfs/super.c index ac2ec5ef66e4..4abb1047c689 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -432,7 +432,7 @@ static struct file_system_type hfs_fs_type = { .fs_flags = FS_REQUIRES_DEV, }; -static void hfs_init_once(struct kmem_cache *cachep, void *p) +static void hfs_init_once(void *p) { struct hfs_inode_info *i = p; diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 3859118531c7..e834e578c93f 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -485,7 +485,7 @@ static struct file_system_type hfsplus_fs_type = { .fs_flags = FS_REQUIRES_DEV, }; -static void hfsplus_init_once(struct kmem_cache *cachep, void *p) +static void hfsplus_init_once(void *p) { struct hfsplus_inode_info *i = p; diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index f63a699ec659..b8ae9c90ada0 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -173,7 +173,7 @@ static void hpfs_destroy_inode(struct inode *inode) kmem_cache_free(hpfs_inode_cachep, hpfs_i(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo; diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index dbd01d262ca4..3f58923fb39b 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -705,7 +705,7 @@ static const struct address_space_operations hugetlbfs_aops = { }; -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct hugetlbfs_inode_info *ei = (struct hugetlbfs_inode_info *)foo; diff --git a/fs/inode.c b/fs/inode.c index 35b6414522ea..b6726f644530 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -224,7 +224,7 @@ void inode_init_once(struct inode *inode) EXPORT_SYMBOL(inode_init_once); -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct inode * inode = (struct inode *) foo; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 044a254d526b..26948a6033b6 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -73,7 +73,7 @@ static void isofs_destroy_inode(struct inode *inode) kmem_cache_free(isofs_inode_cachep, ISOFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct iso_inode_info *ei = foo; diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 7da69eae49e4..efd401257ed9 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -44,7 +44,7 @@ static void jffs2_destroy_inode(struct inode *inode) kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode)); } -static void jffs2_i_init_once(struct kmem_cache *cachep, void *foo) +static void jffs2_i_init_once(void *foo) { struct jffs2_inode_info *f = foo; diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 854ff0ec574f..c350057087dd 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -182,7 +182,7 @@ static inline void remove_metapage(struct page *page, struct metapage *mp) #endif -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct metapage *mp = (struct metapage *)foo; diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 359c091d8965..3630718be395 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -760,7 +760,7 @@ static struct file_system_type jfs_fs_type = { .fs_flags = FS_REQUIRES_DEV, }; -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct jfs_inode_info *jfs_ip = (struct jfs_inode_info *) foo; diff --git a/fs/locks.c b/fs/locks.c index 01490300f7cb..5eb259e3cd38 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -201,7 +201,7 @@ EXPORT_SYMBOL(locks_init_lock); * Initialises the fields of the file lock which are invariant for * free file_locks. */ -static void init_once(struct kmem_cache *cache, void *foo) +static void init_once(void *foo) { struct file_lock *lock = (struct file_lock *) foo; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 523d73713418..d1d1eb84679d 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -68,7 +68,7 @@ static void minix_destroy_inode(struct inode *inode) kmem_cache_free(minix_inode_cachep, minix_i(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct minix_inode_info *ei = (struct minix_inode_info *) foo; diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 2e5ab1204dec..d642f0e5b365 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -64,7 +64,7 @@ static void ncp_destroy_inode(struct inode *inode) kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct ncp_inode_info *ei = (struct ncp_inode_info *) foo; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index df23f987da6b..52daefa2f521 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1242,7 +1242,7 @@ static inline void nfs4_init_once(struct nfs_inode *nfsi) #endif } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct nfs_inode *nfsi = (struct nfs_inode *) foo; diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 3e76f3b216bc..4a46743b5077 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -3080,7 +3080,7 @@ struct kmem_cache *ntfs_inode_cache; struct kmem_cache *ntfs_big_inode_cache; /* Init once constructor for the inode slab cache. */ -static void ntfs_big_inode_init_once(struct kmem_cache *cachep, void *foo) +static void ntfs_big_inode_init_once(void *foo) { ntfs_inode *ni = (ntfs_inode *)foo; diff --git a/fs/ocfs2/dlm/dlmfs.c b/fs/ocfs2/dlm/dlmfs.c index e48aba698b77..533a789c3ef8 100644 --- a/fs/ocfs2/dlm/dlmfs.c +++ b/fs/ocfs2/dlm/dlmfs.c @@ -267,8 +267,7 @@ static ssize_t dlmfs_file_write(struct file *filp, return writelen; } -static void dlmfs_init_once(struct kmem_cache *cachep, - void *foo) +static void dlmfs_init_once(void *foo) { struct dlmfs_inode_private *ip = (struct dlmfs_inode_private *) foo; diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index ccecfe5094fa..2560b33889aa 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1118,7 +1118,7 @@ bail: return status; } -static void ocfs2_inode_init_once(struct kmem_cache *cachep, void *data) +static void ocfs2_inode_init_once(void *data) { struct ocfs2_inode_info *oi = data; diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index d17b4fd204e1..9f5b054f06b9 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -430,7 +430,7 @@ static struct file_system_type openprom_fs_type = { .kill_sb = kill_anon_super, }; -static void op_inode_init_once(struct kmem_cache * cachep, void *data) +static void op_inode_init_once(void *data) { struct op_inode_info *oi = (struct op_inode_info *) data; diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 02eca2ed9dd7..b37f25dc45a5 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -94,7 +94,7 @@ static void proc_destroy_inode(struct inode *inode) kmem_cache_free(proc_inode_cachep, PROC_I(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct proc_inode *ei = (struct proc_inode *) foo; diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index b31ab78052b3..2aad1044b84c 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -553,7 +553,7 @@ static void qnx4_destroy_inode(struct inode *inode) kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo; diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 2ec748ba0bd3..879e54d35c2d 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -521,7 +521,7 @@ static void reiserfs_destroy_inode(struct inode *inode) kmem_cache_free(reiserfs_inode_cachep, REISERFS_I(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct reiserfs_inode_info *ei = (struct reiserfs_inode_info *)foo; diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 3f13d491c7c7..8e51a2aaa977 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -577,7 +577,7 @@ static void romfs_destroy_inode(struct inode *inode) kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct romfs_inode_info *ei = foo; diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 376ef3ee6ed7..3528f40ffb0f 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -67,7 +67,7 @@ static void smb_destroy_inode(struct inode *inode) kmem_cache_free(smb_inode_cachep, SMB_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct smb_inode_info *ei = (struct smb_inode_info *) foo; diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index c5d60de0658f..df0d435baa48 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -326,7 +326,7 @@ static void sysv_destroy_inode(struct inode *inode) kmem_cache_free(sysv_inode_cachep, SYSV_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *p) +static void init_once(void *p) { struct sysv_inode_info *si = (struct sysv_inode_info *)p; diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 00eb9c68ad03..ca1e2d4e03cc 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1841,7 +1841,7 @@ static struct file_system_type ubifs_fs_type = { /* * Inode slab cache constructor. */ -static void inode_slab_ctor(struct kmem_cache *cachep, void *obj) +static void inode_slab_ctor(void *obj) { struct ubifs_inode *ui = obj; inode_init_once(&ui->vfs_inode); diff --git a/fs/udf/super.c b/fs/udf/super.c index 44cc702f96cc..5698bbf83bbf 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -148,7 +148,7 @@ static void udf_destroy_inode(struct inode *inode) kmem_cache_free(udf_inode_cachep, UDF_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct udf_inode_info *ei = (struct udf_inode_info *)foo; diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 227c9d700040..3e30e40aa24d 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1302,7 +1302,7 @@ static void ufs_destroy_inode(struct inode *inode) kmem_cache_free(ufs_inode_cachep, UFS_I(inode)); } -static void init_once(struct kmem_cache * cachep, void *foo) +static void init_once(void *foo) { struct ufs_inode_info *ei = (struct ufs_inode_info *) foo; diff --git a/fs/xfs/linux-2.6/kmem.h b/fs/xfs/linux-2.6/kmem.h index 5e9564902976..a20683cf74dd 100644 --- a/fs/xfs/linux-2.6/kmem.h +++ b/fs/xfs/linux-2.6/kmem.h @@ -79,7 +79,7 @@ kmem_zone_init(int size, char *zone_name) static inline kmem_zone_t * kmem_zone_init_flags(int size, char *zone_name, unsigned long flags, - void (*construct)(kmem_zone_t *, void *)) + void (*construct)(void *)) { return kmem_cache_create(zone_name, size, 0, flags, construct); } diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 742b2c7852c1..943381284e2e 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -843,7 +843,6 @@ xfs_fs_destroy_inode( STATIC void xfs_fs_inode_init_once( - kmem_zone_t *zonep, void *vnode) { inode_init_once(vn_to_inode((bhv_vnode_t *)vnode)); diff --git a/include/linux/slab.h b/include/linux/slab.h index 41103910f8a2..9ff8e8499403 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -58,7 +58,7 @@ int slab_is_available(void); struct kmem_cache *kmem_cache_create(const char *, size_t, size_t, unsigned long, - void (*)(struct kmem_cache *, void *)); + void (*)(void *)); void kmem_cache_destroy(struct kmem_cache *); int kmem_cache_shrink(struct kmem_cache *); void kmem_cache_free(struct kmem_cache *, void *); diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index d117ea2825a9..5bad61a93f65 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -85,7 +85,7 @@ struct kmem_cache { struct kmem_cache_order_objects min; gfp_t allocflags; /* gfp flags to use on each alloc */ int refcount; /* Refcount for slab cache destroy */ - void (*ctor)(struct kmem_cache *, void *); + void (*ctor)(void *); int inuse; /* Offset to metadata */ int align; /* Alignment */ const char *name; /* Name (only for display!) */ diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 1fdc2eb2f6d8..474984f9e032 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -207,7 +207,7 @@ static int mqueue_get_sb(struct file_system_type *fs_type, return get_sb_single(fs_type, flags, data, mqueue_fill_super, mnt); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct mqueue_inode_info *p = (struct mqueue_inode_info *) foo; diff --git a/kernel/fork.c b/kernel/fork.c index b99d73e971a4..80e83e459b17 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1442,7 +1442,7 @@ long do_fork(unsigned long clone_flags, #define ARCH_MIN_MMSTRUCT_ALIGN 0 #endif -static void sighand_ctor(struct kmem_cache *cachep, void *data) +static void sighand_ctor(void *data) { struct sighand_struct *sighand = data; diff --git a/lib/idr.c b/lib/idr.c index 3476f8203e97..e728c7fccc4d 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -607,7 +607,7 @@ void *idr_replace(struct idr *idp, void *ptr, int id) } EXPORT_SYMBOL(idr_replace); -static void idr_cache_ctor(struct kmem_cache *idr_layer_cache, void *idr_layer) +static void idr_cache_ctor(void *idr_layer) { memset(idr_layer, 0, sizeof(struct idr_layer)); } diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 9c4f1ffa2864..be86b32bc874 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -1183,7 +1183,7 @@ int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag) EXPORT_SYMBOL(radix_tree_tagged); static void -radix_tree_node_ctor(struct kmem_cache *cachep, void *node) +radix_tree_node_ctor(void *node) { memset(node, 0, sizeof(struct radix_tree_node)); } diff --git a/mm/rmap.c b/mm/rmap.c index abbd29f7c43f..39ae5a9bf382 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -138,7 +138,7 @@ void anon_vma_unlink(struct vm_area_struct *vma) anon_vma_free(anon_vma); } -static void anon_vma_ctor(struct kmem_cache *cachep, void *data) +static void anon_vma_ctor(void *data) { struct anon_vma *anon_vma = data; diff --git a/mm/shmem.c b/mm/shmem.c index 1089092aecaf..952d361774bb 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2352,7 +2352,7 @@ static void shmem_destroy_inode(struct inode *inode) kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct shmem_inode_info *p = (struct shmem_inode_info *) foo; diff --git a/mm/slab.c b/mm/slab.c index 052e7d64537e..918f04f7fef1 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -406,7 +406,7 @@ struct kmem_cache { unsigned int dflags; /* dynamic flags */ /* constructor func */ - void (*ctor)(struct kmem_cache *, void *); + void (*ctor)(void *obj); /* 5) cache creation/removal */ const char *name; @@ -2137,8 +2137,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep) */ struct kmem_cache * kmem_cache_create (const char *name, size_t size, size_t align, - unsigned long flags, - void (*ctor)(struct kmem_cache *, void *)) + unsigned long flags, void (*ctor)(void *)) { size_t left_over, slab_size, ralign; struct kmem_cache *cachep = NULL, *pc; @@ -2653,7 +2652,7 @@ static void cache_init_objs(struct kmem_cache *cachep, * They must also be threaded. */ if (cachep->ctor && !(cachep->flags & SLAB_POISON)) - cachep->ctor(cachep, objp + obj_offset(cachep)); + cachep->ctor(objp + obj_offset(cachep)); if (cachep->flags & SLAB_RED_ZONE) { if (*dbg_redzone2(cachep, objp) != RED_INACTIVE) @@ -2669,7 +2668,7 @@ static void cache_init_objs(struct kmem_cache *cachep, cachep->buffer_size / PAGE_SIZE, 0); #else if (cachep->ctor) - cachep->ctor(cachep, objp); + cachep->ctor(objp); #endif slab_bufctl(slabp)[i] = i + 1; } @@ -3093,7 +3092,7 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep, #endif objp += obj_offset(cachep); if (cachep->ctor && cachep->flags & SLAB_POISON) - cachep->ctor(cachep, objp); + cachep->ctor(objp); #if ARCH_SLAB_MINALIGN if ((u32)objp & (ARCH_SLAB_MINALIGN-1)) { printk(KERN_ERR "0x%p: not aligned to ARCH_SLAB_MINALIGN=%d\n", diff --git a/mm/slob.c b/mm/slob.c index de268eb7ac70..d8fbd4d1bfa7 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -525,12 +525,11 @@ struct kmem_cache { unsigned int size, align; unsigned long flags; const char *name; - void (*ctor)(struct kmem_cache *, void *); + void (*ctor)(void *); }; struct kmem_cache *kmem_cache_create(const char *name, size_t size, - size_t align, unsigned long flags, - void (*ctor)(struct kmem_cache *, void *)) + size_t align, unsigned long flags, void (*ctor)(void *)) { struct kmem_cache *c; @@ -575,7 +574,7 @@ void *kmem_cache_alloc_node(struct kmem_cache *c, gfp_t flags, int node) b = slob_new_page(flags, get_order(c->size), node); if (c->ctor) - c->ctor(c, b); + c->ctor(b); return b; } diff --git a/mm/slub.c b/mm/slub.c index 77c21cf53ff9..b7e2cd5d82db 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1012,7 +1012,7 @@ __setup("slub_debug", setup_slub_debug); static unsigned long kmem_cache_flags(unsigned long objsize, unsigned long flags, const char *name, - void (*ctor)(struct kmem_cache *, void *)) + void (*ctor)(void *)) { /* * Enable debugging if selected on the kernel commandline. @@ -1040,7 +1040,7 @@ static inline int check_object(struct kmem_cache *s, struct page *page, static inline void add_full(struct kmem_cache_node *n, struct page *page) {} static inline unsigned long kmem_cache_flags(unsigned long objsize, unsigned long flags, const char *name, - void (*ctor)(struct kmem_cache *, void *)) + void (*ctor)(void *)) { return flags; } @@ -1103,7 +1103,7 @@ static void setup_object(struct kmem_cache *s, struct page *page, { setup_object_debug(s, page, object); if (unlikely(s->ctor)) - s->ctor(s, object); + s->ctor(object); } static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) @@ -2286,7 +2286,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags, const char *name, size_t size, size_t align, unsigned long flags, - void (*ctor)(struct kmem_cache *, void *)) + void (*ctor)(void *)) { memset(s, 0, kmem_size); s->name = name; @@ -3042,7 +3042,7 @@ static int slab_unmergeable(struct kmem_cache *s) static struct kmem_cache *find_mergeable(size_t size, size_t align, unsigned long flags, const char *name, - void (*ctor)(struct kmem_cache *, void *)) + void (*ctor)(void *)) { struct kmem_cache *s; @@ -3082,8 +3082,7 @@ static struct kmem_cache *find_mergeable(size_t size, } struct kmem_cache *kmem_cache_create(const char *name, size_t size, - size_t align, unsigned long flags, - void (*ctor)(struct kmem_cache *, void *)) + size_t align, unsigned long flags, void (*ctor)(void *)) { struct kmem_cache *s; diff --git a/net/socket.c b/net/socket.c index 1310a82cbba7..8ef8ba81b9e2 100644 --- a/net/socket.c +++ b/net/socket.c @@ -265,7 +265,7 @@ static void sock_destroy_inode(struct inode *inode) container_of(inode, struct socket_alloc, vfs_inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct socket_alloc *ei = (struct socket_alloc *)foo; diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 5a9b0e7828cd..23a2b8f6dc49 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -897,7 +897,7 @@ static struct file_system_type rpc_pipe_fs_type = { }; static void -init_once(struct kmem_cache * cachep, void *foo) +init_once(void *foo) { struct rpc_inode *rpci = (struct rpc_inode *) foo; -- cgit From 88ac2921a71f788ed693bcd44731dd6bc1994640 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:43 -0700 Subject: tracehook: add linux/tracehook.h This patch series introduces the "tracehook" interface layer of inlines in . There are more details in the log entry for patch 01/23 and in the header file comments inside that patch. Most of these changes move code around with little or no change, and they should not break anything or change any behavior. This sets a new standard for uniform arch support to enable clean arch-independent implementations of new debugging and tracing stuff, denoted by CONFIG_HAVE_ARCH_TRACEHOOK. Patch 20/23 adds that symbol to arch/Kconfig, with comments listing everything an arch has to do before setting "select HAVE_ARCH_TRACEHOOK". These are elaborted a bit at: http://sourceware.org/systemtap/wiki/utrace/arch/HowTo The new inlines that arch code must define or call have detailed kerneldoc comments in the generic header files that say what is required. No arch is obligated to do any work, and no arch's build should be broken by these changes. There are several steps that each arch should take so it can set HAVE_ARCH_TRACEHOOK. Most of these are simple. Providing this support will let new things people add for doing debugging and tracing of user-level threads "just work" for your arch in the future. For an arch that does not provide HAVE_ARCH_TRACEHOOK, some new options for such features will not be available for config. I have done some arch work and will submit this to the arch maintainers after the generic tracehook series settles in. For now, that work is available in my GIT repositories, and in patch and mbox-of-patches form at http://people.redhat.com/roland/utrace/2.6-current/ This paves the way for my "utrace" work, to be submitted later. But it is not innately tied to that. I hope that the tracehook series can go in soon regardless of what eventually does or doesn't go on top of it. For anyone implementing any kind of new tracing/debugging plan, or just understanding all the context of the existing ptrace implementation, having tracehook.h makes things much easier to find and understand. This patch: This adds the new kernel-internal header file . This is not yet used at all. The comments in the header introduce what the following series of patches is about. The aim is to formalize and consolidate all the places that the core kernel code and the arch code now ties into the ptrace implementation. These patches mostly don't cause any functional change. They just move the details of ptrace logic out of core code into tracehook.h inlines, where they are mostly compiled away to the same as before. All that changes is that everything is thoroughly documented and any future reworking of ptrace, or addition of something new, would not have to touch core code all over, just change the tracehook.h inlines. The new linux/ptrace.h inlines are used by the following patches in the new tracehook_*() inlines. Using these helpers for the ptrace event stops makes it simple to change or disable the old ptrace implementation of these stops conditionally later. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 33 ++++++++++++++++++++++++++++++ include/linux/tracehook.h | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 include/linux/tracehook.h (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index c6f5f9dd0cee..c74abfc4c7e8 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -121,6 +121,39 @@ static inline void ptrace_unlink(struct task_struct *child) int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data); int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data); +/** + * task_ptrace - return %PT_* flags that apply to a task + * @task: pointer to &task_struct in question + * + * Returns the %PT_* flags that apply to @task. + */ +static inline int task_ptrace(struct task_struct *task) +{ + return task->ptrace; +} + +/** + * ptrace_event - possibly stop for a ptrace event notification + * @mask: %PT_* bit to check in @current->ptrace + * @event: %PTRACE_EVENT_* value to report if @mask is set + * @message: value for %PTRACE_GETEVENTMSG to return + * + * This checks the @mask bit to see if ptrace wants stops for this event. + * If so we stop, reporting @event and @message to the ptrace parent. + * + * Returns nonzero if we did a ptrace notification, zero if not. + * + * Called without locks. + */ +static inline int ptrace_event(int mask, int event, unsigned long message) +{ + if (mask && likely(!(current->ptrace & mask))) + return 0; + current->ptrace_message = message; + ptrace_notify((event << 8) | SIGTRAP); + return 1; +} + #ifndef force_successful_syscall_return /* * System call handlers that, upon successful completion, need to return a diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h new file mode 100644 index 000000000000..bea0f3eeff54 --- /dev/null +++ b/include/linux/tracehook.h @@ -0,0 +1,52 @@ +/* + * Tracing hooks + * + * Copyright (C) 2008 Red Hat, Inc. All rights reserved. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * This file defines hook entry points called by core code where + * user tracing/debugging support might need to do something. These + * entry points are called tracehook_*(). Each hook declared below + * has a detailed kerneldoc comment giving the context (locking et + * al) from which it is called, and the meaning of its return value. + * + * Each function here typically has only one call site, so it is ok + * to have some nontrivial tracehook_*() inlines. In all cases, the + * fast path when no tracing is enabled should be very short. + * + * The purpose of this file and the tracehook_* layer is to consolidate + * the interface that the kernel core and arch code uses to enable any + * user debugging or tracing facility (such as ptrace). The interfaces + * here are carefully documented so that maintainers of core and arch + * code do not need to think about the implementation details of the + * tracing facilities. Likewise, maintainers of the tracing code do not + * need to understand all the calling core or arch code in detail, just + * documented circumstances of each call, such as locking conditions. + * + * If the calling core code changes so that locking is different, then + * it is ok to change the interface documented here. The maintainer of + * core code changing should notify the maintainers of the tracing code + * that they need to work out the change. + * + * Some tracehook_*() inlines take arguments that the current tracing + * implementations might not necessarily use. These function signatures + * are chosen to pass in all the information that is on hand in the + * caller and might conceivably be relevant to a tracer, so that the + * core code won't have to be updated when tracing adds more features. + * If a call site changes so that some of those parameters are no longer + * already on hand without extra work, then the tracehook_* interface + * can change so there is no make-work burden on the core code. The + * maintainer of core code changing should notify the maintainers of the + * tracing code that they need to work out the change. + */ + +#ifndef _LINUX_TRACEHOOK_H +#define _LINUX_TRACEHOOK_H 1 + +#include +#include + +#endif /* */ -- cgit From 6341c393fcc37d58727865f1ee2f65e632e9d4f0 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:44 -0700 Subject: tracehook: exec This moves all the ptrace hooks related to exec into tracehook.h inlines. This also lifts the calls for tracing out of the binfmt load_binary hooks into search_binary_handler() after it calls into the binfmt module. This change has no effect, since all the binfmt modules' load_binary functions did the call at the end on success, and now search_binary_handler() does it immediately after return if successful. We consolidate the repeated code, and binfmt modules no longer need to import ptrace_notify(). Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/ia32/ia32_aout.c | 6 ------ fs/binfmt_aout.c | 6 ------ fs/binfmt_elf.c | 6 ------ fs/binfmt_elf_fdpic.c | 7 ------- fs/binfmt_flat.c | 3 --- fs/binfmt_som.c | 2 -- fs/exec.c | 12 ++++-------- include/linux/tracehook.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 50 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/ia32/ia32_aout.c b/arch/x86/ia32/ia32_aout.c index 58cccb6483b0..a0e1dbe67dc1 100644 --- a/arch/x86/ia32/ia32_aout.c +++ b/arch/x86/ia32/ia32_aout.c @@ -441,12 +441,6 @@ beyond_if: regs->r8 = regs->r9 = regs->r10 = regs->r11 = regs->r12 = regs->r13 = regs->r14 = regs->r15 = 0; set_fs(USER_DS); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } return 0; } diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index ba4cddb92f1d..204cfd1d7676 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -444,12 +444,6 @@ beyond_if: regs->gp = ex.a_gpvalue; #endif start_thread(regs, ex.a_entry, current->mm->start_stack); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } return 0; } diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 3b6ff854d983..655ed8d30a86 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1003,12 +1003,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) #endif start_thread(regs, elf_entry, bprm->p); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } retval = 0; out: kfree(loc); diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 1b59b1edf26d..fdeadab2f18b 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -433,13 +433,6 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, entryaddr = interp_params.entry_addr ?: exec_params.entry_addr; start_thread(regs, entryaddr, current->mm->start_stack); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } - retval = 0; error: diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c index 2cb1acda3a82..56372ecf1690 100644 --- a/fs/binfmt_flat.c +++ b/fs/binfmt_flat.c @@ -920,9 +920,6 @@ static int load_flat_binary(struct linux_binprm * bprm, struct pt_regs * regs) start_thread(regs, start_addr, current->mm->start_stack); - if (current->ptrace & PT_PTRACED) - send_sig(SIGTRAP, current, 0); - return 0; } diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c index fdc36bfd6a7b..68be580ba289 100644 --- a/fs/binfmt_som.c +++ b/fs/binfmt_som.c @@ -274,8 +274,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs) map_hpux_gateway_page(current,current->mm); start_thread_som(regs, som_entry, bprm->p); - if (current->ptrace & PT_PTRACED) - send_sig(SIGTRAP, current, 0); return 0; /* error cleanup */ diff --git a/fs/exec.c b/fs/exec.c index 5e559013e303..b8792a131533 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -42,13 +42,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include @@ -1071,13 +1071,8 @@ EXPORT_SYMBOL(prepare_binprm); static int unsafe_exec(struct task_struct *p) { - int unsafe = 0; - if (p->ptrace & PT_PTRACED) { - if (p->ptrace & PT_PTRACE_CAP) - unsafe |= LSM_UNSAFE_PTRACE_CAP; - else - unsafe |= LSM_UNSAFE_PTRACE; - } + int unsafe = tracehook_unsafe_exec(p); + if (atomic_read(&p->fs->count) > 1 || atomic_read(&p->files->count) > 1 || atomic_read(&p->sighand->count) > 1) @@ -1214,6 +1209,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) read_unlock(&binfmt_lock); retval = fn(bprm, regs); if (retval >= 0) { + tracehook_report_exec(fmt, bprm, regs); put_binfmt(fmt); allow_write_access(bprm->file); if (bprm->file) diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index bea0f3eeff54..6276353709c1 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -48,5 +48,51 @@ #include #include +#include +struct linux_binprm; + +/** + * tracehook_unsafe_exec - check for exec declared unsafe due to tracing + * @task: current task doing exec + * + * Return %LSM_UNSAFE_* bits applied to an exec because of tracing. + * + * Called with task_lock() held on @task. + */ +static inline int tracehook_unsafe_exec(struct task_struct *task) +{ + int unsafe = 0; + int ptrace = task_ptrace(task); + if (ptrace & PT_PTRACED) { + if (ptrace & PT_PTRACE_CAP) + unsafe |= LSM_UNSAFE_PTRACE_CAP; + else + unsafe |= LSM_UNSAFE_PTRACE; + } + return unsafe; +} + +/** + * tracehook_report_exec - a successful exec was completed + * @fmt: &struct linux_binfmt that performed the exec + * @bprm: &struct linux_binprm containing exec details + * @regs: user-mode register state + * + * An exec just completed, we are shortly going to return to user mode. + * The freshly initialized register state can be seen and changed in @regs. + * The name, file and other pointers in @bprm are still on hand to be + * inspected, but will be freed as soon as this returns. + * + * Called with no locks, but with some kernel resources held live + * and a reference on @fmt->module. + */ +static inline void tracehook_report_exec(struct linux_binfmt *fmt, + struct linux_binprm *bprm, + struct pt_regs *regs) +{ + if (!ptrace_event(PT_TRACE_EXEC, PTRACE_EVENT_EXEC, 0) && + unlikely(task_ptrace(current) & PT_PTRACED)) + send_sig(SIGTRAP, current, 0); +} #endif /* */ -- cgit From 30199f5a46aee204bf437a4f5b0740f3efe448b7 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:46 -0700 Subject: tracehook: exit This moves the PTRACE_EVENT_EXIT tracing into a tracehook.h inline, tracehook_report_exec(). The change has no effect, just clean-up. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 15 +++++++++++++++ kernel/exit.c | 6 ++---- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 6276353709c1..967ab473afbc 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -95,4 +95,19 @@ static inline void tracehook_report_exec(struct linux_binfmt *fmt, send_sig(SIGTRAP, current, 0); } +/** + * tracehook_report_exit - task has begun to exit + * @exit_code: pointer to value destined for @current->exit_code + * + * @exit_code points to the value passed to do_exit(), which tracing + * might change here. This is almost the first thing in do_exit(), + * before freeing any resources or setting the %PF_EXITING flag. + * + * Called with no locks held. + */ +static inline void tracehook_report_exit(long *exit_code) +{ + ptrace_event(PT_TRACE_EXIT, PTRACE_EVENT_EXIT, *exit_code); +} + #endif /* */ diff --git a/kernel/exit.c b/kernel/exit.c index ad933bb29ec7..c3691cbc220a 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -1029,10 +1030,7 @@ NORET_TYPE void do_exit(long code) if (unlikely(!tsk->pid)) panic("Attempted to kill the idle task!"); - if (unlikely(current->ptrace & PT_TRACE_EXIT)) { - current->ptrace_message = code; - ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP); - } + tracehook_report_exit(&code); /* * We're taking recursive faults here in do_exit. Safest is to just -- cgit From 09a05394fe2448a4139b014936330af23fa7ec83 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:47 -0700 Subject: tracehook: clone This moves all the ptrace initialization and tracing logic for task creation into tracehook.h and ptrace.h inlines. It reorganizes the code slightly, but should not change any behavior. There are four tracehook entry points, at each important stage of task creation. This keeps the interface from the core fork.c code fairly clean, while supporting the complex setup required for ptrace or something like it. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 22 ++++++++++ include/linux/tracehook.h | 100 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/fork.c | 69 +++++++++++++------------------- 3 files changed, 150 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index c74abfc4c7e8..dae6d85520fb 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -154,6 +154,28 @@ static inline int ptrace_event(int mask, int event, unsigned long message) return 1; } +/** + * ptrace_init_task - initialize ptrace state for a new child + * @child: new child task + * @ptrace: true if child should be ptrace'd by parent's tracer + * + * This is called immediately after adding @child to its parent's children + * list. @ptrace is false in the normal case, and true to ptrace @child. + * + * Called with current's siglock and write_lock_irq(&tasklist_lock) held. + */ +static inline void ptrace_init_task(struct task_struct *child, bool ptrace) +{ + INIT_LIST_HEAD(&child->ptrace_entry); + INIT_LIST_HEAD(&child->ptraced); + child->parent = child->real_parent; + child->ptrace = 0; + if (unlikely(ptrace)) { + child->ptrace = current->ptrace; + __ptrace_link(child, current->parent); + } +} + #ifndef force_successful_syscall_return /* * System call handlers that, upon successful completion, need to return a diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 967ab473afbc..3ebc58b59766 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -110,4 +110,104 @@ static inline void tracehook_report_exit(long *exit_code) ptrace_event(PT_TRACE_EXIT, PTRACE_EVENT_EXIT, *exit_code); } +/** + * tracehook_prepare_clone - prepare for new child to be cloned + * @clone_flags: %CLONE_* flags from clone/fork/vfork system call + * + * This is called before a new user task is to be cloned. + * Its return value will be passed to tracehook_finish_clone(). + * + * Called with no locks held. + */ +static inline int tracehook_prepare_clone(unsigned clone_flags) +{ + if (clone_flags & CLONE_UNTRACED) + return 0; + + if (clone_flags & CLONE_VFORK) { + if (current->ptrace & PT_TRACE_VFORK) + return PTRACE_EVENT_VFORK; + } else if ((clone_flags & CSIGNAL) != SIGCHLD) { + if (current->ptrace & PT_TRACE_CLONE) + return PTRACE_EVENT_CLONE; + } else if (current->ptrace & PT_TRACE_FORK) + return PTRACE_EVENT_FORK; + + return 0; +} + +/** + * tracehook_finish_clone - new child created and being attached + * @child: new child task + * @clone_flags: %CLONE_* flags from clone/fork/vfork system call + * @trace: return value from tracehook_clone_prepare() + * + * This is called immediately after adding @child to its parent's children list. + * The @trace value is that returned by tracehook_prepare_clone(). + * + * Called with current's siglock and write_lock_irq(&tasklist_lock) held. + */ +static inline void tracehook_finish_clone(struct task_struct *child, + unsigned long clone_flags, int trace) +{ + ptrace_init_task(child, (clone_flags & CLONE_PTRACE) || trace); +} + +/** + * tracehook_report_clone - in parent, new child is about to start running + * @trace: return value from tracehook_clone_prepare() + * @regs: parent's user register state + * @clone_flags: flags from parent's system call + * @pid: new child's PID in the parent's namespace + * @child: new child task + * + * Called after a child is set up, but before it has been started running. + * The @trace value is that returned by tracehook_clone_prepare(). + * This is not a good place to block, because the child has not started yet. + * Suspend the child here if desired, and block in tracehook_clone_complete(). + * This must prevent the child from self-reaping if tracehook_clone_complete() + * uses the @child pointer; otherwise it might have died and been released by + * the time tracehook_report_clone_complete() is called. + * + * Called with no locks held, but the child cannot run until this returns. + */ +static inline void tracehook_report_clone(int trace, struct pt_regs *regs, + unsigned long clone_flags, + pid_t pid, struct task_struct *child) +{ + if (unlikely(trace)) { + /* + * The child starts up with an immediate SIGSTOP. + */ + sigaddset(&child->pending.signal, SIGSTOP); + set_tsk_thread_flag(child, TIF_SIGPENDING); + } +} + +/** + * tracehook_report_clone_complete - new child is running + * @trace: return value from tracehook_clone_prepare() + * @regs: parent's user register state + * @clone_flags: flags from parent's system call + * @pid: new child's PID in the parent's namespace + * @child: child task, already running + * + * This is called just after the child has started running. This is + * just before the clone/fork syscall returns, or blocks for vfork + * child completion if @clone_flags has the %CLONE_VFORK bit set. + * The @child pointer may be invalid if a self-reaping child died and + * tracehook_report_clone() took no action to prevent it from self-reaping. + * + * Called with no locks held. + */ +static inline void tracehook_report_clone_complete(int trace, + struct pt_regs *regs, + unsigned long clone_flags, + pid_t pid, + struct task_struct *child) +{ + if (unlikely(trace)) + ptrace_event(0, trace, pid); +} + #endif /* */ diff --git a/kernel/fork.c b/kernel/fork.c index 80e83e459b17..b42f8ed23611 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -865,8 +866,7 @@ static void copy_flags(unsigned long clone_flags, struct task_struct *p) new_flags &= ~PF_SUPERPRIV; new_flags |= PF_FORKNOEXEC; - if (!(clone_flags & CLONE_PTRACE)) - p->ptrace = 0; + new_flags |= PF_STARTING; p->flags = new_flags; clear_freeze_flag(p); } @@ -907,7 +907,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, struct pt_regs *regs, unsigned long stack_size, int __user *child_tidptr, - struct pid *pid) + struct pid *pid, + int trace) { int retval; struct task_struct *p; @@ -1163,8 +1164,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, */ p->group_leader = p; INIT_LIST_HEAD(&p->thread_group); - INIT_LIST_HEAD(&p->ptrace_entry); - INIT_LIST_HEAD(&p->ptraced); /* Now that the task is set up, run cgroup callbacks if * necessary. We need to run them before the task is visible @@ -1195,7 +1194,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->real_parent = current->real_parent; else p->real_parent = current; - p->parent = p->real_parent; spin_lock(¤t->sighand->siglock); @@ -1237,8 +1235,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, if (likely(p->pid)) { list_add_tail(&p->sibling, &p->real_parent->children); - if (unlikely(p->ptrace & PT_PTRACED)) - __ptrace_link(p, current->parent); + tracehook_finish_clone(p, clone_flags, trace); if (thread_group_leader(p)) { if (clone_flags & CLONE_NEWPID) @@ -1323,29 +1320,13 @@ struct task_struct * __cpuinit fork_idle(int cpu) struct pt_regs regs; task = copy_process(CLONE_VM, 0, idle_regs(®s), 0, NULL, - &init_struct_pid); + &init_struct_pid, 0); if (!IS_ERR(task)) init_idle(task, cpu); return task; } -static int fork_traceflag(unsigned clone_flags) -{ - if (clone_flags & CLONE_UNTRACED) - return 0; - else if (clone_flags & CLONE_VFORK) { - if (current->ptrace & PT_TRACE_VFORK) - return PTRACE_EVENT_VFORK; - } else if ((clone_flags & CSIGNAL) != SIGCHLD) { - if (current->ptrace & PT_TRACE_CLONE) - return PTRACE_EVENT_CLONE; - } else if (current->ptrace & PT_TRACE_FORK) - return PTRACE_EVENT_FORK; - - return 0; -} - /* * Ok, this is the main fork-routine. * @@ -1380,14 +1361,14 @@ long do_fork(unsigned long clone_flags, } } - if (unlikely(current->ptrace)) { - trace = fork_traceflag (clone_flags); - if (trace) - clone_flags |= CLONE_PTRACE; - } + /* + * When called from kernel_thread, don't do user tracing stuff. + */ + if (likely(user_mode(regs))) + trace = tracehook_prepare_clone(clone_flags); p = copy_process(clone_flags, stack_start, regs, stack_size, - child_tidptr, NULL); + child_tidptr, NULL, trace); /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. @@ -1405,24 +1386,30 @@ long do_fork(unsigned long clone_flags, init_completion(&vfork); } - if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) { + tracehook_report_clone(trace, regs, clone_flags, nr, p); + + /* + * We set PF_STARTING at creation in case tracing wants to + * use this to distinguish a fully live task from one that + * hasn't gotten to tracehook_report_clone() yet. Now we + * clear it and set the child going. + */ + p->flags &= ~PF_STARTING; + + if (unlikely(clone_flags & CLONE_STOPPED)) { /* * We'll start up with an immediate SIGSTOP. */ sigaddset(&p->pending.signal, SIGSTOP); set_tsk_thread_flag(p, TIF_SIGPENDING); - } - - if (!(clone_flags & CLONE_STOPPED)) - wake_up_new_task(p, clone_flags); - else __set_task_state(p, TASK_STOPPED); - - if (unlikely (trace)) { - current->ptrace_message = nr; - ptrace_notify ((trace << 8) | SIGTRAP); + } else { + wake_up_new_task(p, clone_flags); } + tracehook_report_clone_complete(trace, regs, + clone_flags, nr, p); + if (clone_flags & CLONE_VFORK) { freezer_do_not_count(); wait_for_completion(&vfork); -- cgit From daded34be96b1975ff8539ff62ad8b158ce7d842 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:47 -0700 Subject: tracehook: vfork-done This moves the PTRACE_EVENT_VFORK_DONE tracing into a tracehook.h inline, tracehook_report_vfork_done(). The change has no effect, just clean-up. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 18 ++++++++++++++++++ kernel/fork.c | 5 +---- 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 3ebc58b59766..830e6e16097d 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -210,4 +210,22 @@ static inline void tracehook_report_clone_complete(int trace, ptrace_event(0, trace, pid); } +/** + * tracehook_report_vfork_done - vfork parent's child has exited or exec'd + * @child: child task, already running + * @pid: new child's PID in the parent's namespace + * + * Called after a %CLONE_VFORK parent has waited for the child to complete. + * The clone/vfork system call will return immediately after this. + * The @child pointer may be invalid if a self-reaping child died and + * tracehook_report_clone() took no action to prevent it from self-reaping. + * + * Called with no locks held. + */ +static inline void tracehook_report_vfork_done(struct task_struct *child, + pid_t pid) +{ + ptrace_event(PT_TRACE_VFORK_DONE, PTRACE_EVENT_VFORK_DONE, pid); +} + #endif /* */ diff --git a/kernel/fork.c b/kernel/fork.c index b42f8ed23611..abb3ed6298f6 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1414,10 +1414,7 @@ long do_fork(unsigned long clone_flags, freezer_do_not_count(); wait_for_completion(&vfork); freezer_count(); - if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) { - current->ptrace_message = nr; - ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP); - } + tracehook_report_vfork_done(p, nr); } } else { nr = PTR_ERR(p); -- cgit From dae33574dcf5211e1f43c7e45fa29f73ba3e00cb Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:48 -0700 Subject: tracehook: release_task This moves the ptrace-related logic from release_task into tracehook.h and ptrace.h inlines. It provides clean hooks both before and after locking tasklist_lock, for future tracing logic to do more cleanup without the lock. This also changes release_task() itself in the rare "zap_leader" case to set the leader to EXIT_DEAD before iterating. This maintains the invariant that release_task() only ever handles a task in EXIT_DEAD. This is a common-sense invariant that is already always true except in this one arcane case of zombie leader whose parent ignores SIGCHLD. This change is harmless and only costs one store in this one rare case. It keeps the expected state more consisently sane, which is nicer when debugging weirdness in release_task(). It also lets some future code in the tracehook entry points rely on this invariant for bookkeeping. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 13 +++++++++++++ include/linux/tracehook.h | 28 ++++++++++++++++++++++++++++ kernel/exit.c | 21 +++++++++------------ 3 files changed, 50 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index dae6d85520fb..ed69c03692d9 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -176,6 +176,19 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace) } } +/** + * ptrace_release_task - final ptrace-related cleanup of a zombie being reaped + * @task: task in %EXIT_DEAD state + * + * Called with write_lock(&tasklist_lock) held. + */ +static inline void ptrace_release_task(struct task_struct *task) +{ + BUG_ON(!list_empty(&task->ptraced)); + ptrace_unlink(task); + BUG_ON(!list_empty(&task->ptrace_entry)); +} + #ifndef force_successful_syscall_return /* * System call handlers that, upon successful completion, need to return a diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 830e6e16097d..9a5b3be2503a 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -228,4 +228,32 @@ static inline void tracehook_report_vfork_done(struct task_struct *child, ptrace_event(PT_TRACE_VFORK_DONE, PTRACE_EVENT_VFORK_DONE, pid); } +/** + * tracehook_prepare_release_task - task is being reaped, clean up tracing + * @task: task in %EXIT_DEAD state + * + * This is called in release_task() just before @task gets finally reaped + * and freed. This would be the ideal place to remove and clean up any + * tracing-related state for @task. + * + * Called with no locks held. + */ +static inline void tracehook_prepare_release_task(struct task_struct *task) +{ +} + +/** + * tracehook_finish_release_task - task is being reaped, clean up tracing + * @task: task in %EXIT_DEAD state + * + * This is called in release_task() when @task is being in the middle of + * being reaped. After this, there must be no tracing entanglements. + * + * Called with write_lock_irq(&tasklist_lock) held. + */ +static inline void tracehook_finish_release_task(struct task_struct *task) +{ + ptrace_release_task(task); +} + #endif /* */ diff --git a/kernel/exit.c b/kernel/exit.c index c3691cbc220a..da28745f7c38 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -163,27 +163,17 @@ static void delayed_put_task_struct(struct rcu_head *rhp) put_task_struct(container_of(rhp, struct task_struct, rcu)); } -/* - * Do final ptrace-related cleanup of a zombie being reaped. - * - * Called with write_lock(&tasklist_lock) held. - */ -static void ptrace_release_task(struct task_struct *p) -{ - BUG_ON(!list_empty(&p->ptraced)); - ptrace_unlink(p); - BUG_ON(!list_empty(&p->ptrace_entry)); -} void release_task(struct task_struct * p) { struct task_struct *leader; int zap_leader; repeat: + tracehook_prepare_release_task(p); atomic_dec(&p->user->processes); proc_flush_task(p); write_lock_irq(&tasklist_lock); - ptrace_release_task(p); + tracehook_finish_release_task(p); __exit_signal(p); /* @@ -205,6 +195,13 @@ repeat: * that case. */ zap_leader = task_detached(leader); + + /* + * This maintains the invariant that release_task() + * only runs on a task in EXIT_DEAD, just for sanity. + */ + if (zap_leader) + leader->exit_state = EXIT_DEAD; } write_unlock_irq(&tasklist_lock); -- cgit From 0d094efeb1e98010c6b99923f1eb7e17bf1e3a74 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:49 -0700 Subject: tracehook: tracehook_tracer_task This adds the tracehook_tracer_task() hook to consolidate all forms of "Who is using ptrace on me?" logic. This is used for "TracerPid:" in /proc and for permission checks. We also clean up the selinux code the called an identical accessor. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/array.c | 9 +++++++-- fs/proc/base.c | 13 +++++++++---- include/linux/tracehook.h | 18 ++++++++++++++++++ security/selinux/hooks.c | 22 +++------------------- 4 files changed, 37 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/array.c b/fs/proc/array.c index 797d775e0354..0d6eb33597c6 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -80,6 +80,7 @@ #include #include #include +#include #include #include @@ -168,8 +169,12 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, rcu_read_lock(); ppid = pid_alive(p) ? task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0; - tpid = pid_alive(p) && p->ptrace ? - task_pid_nr_ns(rcu_dereference(p->parent), ns) : 0; + tpid = 0; + if (pid_alive(p)) { + struct task_struct *tracer = tracehook_tracer_task(p); + if (tracer) + tpid = task_pid_nr_ns(tracer, ns); + } seq_printf(m, "State:\t%s\n" "Tgid:\t%d\n" diff --git a/fs/proc/base.c b/fs/proc/base.c index a891fe4cb43b..4b74dba69a6d 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -231,10 +232,14 @@ static int check_mem_permission(struct task_struct *task) * If current is actively ptrace'ing, and would also be * permitted to freshly attach with ptrace now, permit it. */ - if (task->parent == current && (task->ptrace & PT_PTRACED) && - task_is_stopped_or_traced(task) && - ptrace_may_access(task, PTRACE_MODE_ATTACH)) - return 0; + if (task_is_stopped_or_traced(task)) { + int match; + rcu_read_lock(); + match = (tracehook_tracer_task(task) == current); + rcu_read_unlock(); + if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH)) + return 0; + } /* * Noone else is allowed. diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 9a5b3be2503a..6468ca0fe69b 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -72,6 +72,24 @@ static inline int tracehook_unsafe_exec(struct task_struct *task) return unsafe; } +/** + * tracehook_tracer_task - return the task that is tracing the given task + * @tsk: task to consider + * + * Returns NULL if noone is tracing @task, or the &struct task_struct + * pointer to its tracer. + * + * Must called under rcu_read_lock(). The pointer returned might be kept + * live only by RCU. During exec, this may be called with task_lock() + * held on @task, still held from when tracehook_unsafe_exec() was called. + */ +static inline struct task_struct *tracehook_tracer_task(struct task_struct *tsk) +{ + if (task_ptrace(tsk) & PT_PTRACED) + return rcu_dereference(tsk->parent); + return NULL; +} + /** * tracehook_report_exec - a successful exec was completed * @fmt: &struct linux_binfmt that performed the exec diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 63f131fc42e4..3481cde5bf15 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include #include @@ -1971,22 +1971,6 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) return __vm_enough_memory(mm, pages, cap_sys_admin); } -/** - * task_tracer_task - return the task that is tracing the given task - * @task: task to consider - * - * Returns NULL if noone is tracing @task, or the &struct task_struct - * pointer to its tracer. - * - * Must be called under rcu_read_lock(). - */ -static struct task_struct *task_tracer_task(struct task_struct *task) -{ - if (task->ptrace & PT_PTRACED) - return rcu_dereference(task->parent); - return NULL; -} - /* binprm security operations */ static int selinux_bprm_alloc_security(struct linux_binprm *bprm) @@ -2238,7 +2222,7 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) u32 ptsid = 0; rcu_read_lock(); - tracer = task_tracer_task(current); + tracer = tracehook_tracer_task(current); if (likely(tracer != NULL)) { sec = tracer->security; ptsid = sec->sid; @@ -5247,7 +5231,7 @@ static int selinux_setprocattr(struct task_struct *p, Otherwise, leave SID unchanged and fail. */ task_lock(p); rcu_read_lock(); - tracer = task_tracer_task(p); + tracer = tracehook_tracer_task(p); if (tracer != NULL) { struct task_security_struct *ptsec = tracer->security; u32 ptsid = ptsec->sid; -- cgit From fa8e26ccd485216fc45c8c2dd1ec3b7ef1a0a2f8 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:50 -0700 Subject: tracehook: tracehook_expect_breakpoints This adds tracehook_expect_breakpoints() as a formal hook for the nommu code to use for its, "Is text-poking likely?" check at mmap time. This names the actual semantics the code means to test, and documents it. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 15 +++++++++++++++ mm/nommu.c | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 6468ca0fe69b..e113e09b0341 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -51,6 +51,21 @@ #include struct linux_binprm; +/** + * tracehook_expect_breakpoints - guess if task memory might be touched + * @task: current task, making a new mapping + * + * Return nonzero if @task is expected to want breakpoint insertion in + * its memory at some point. A zero return is no guarantee it won't + * be done, but this is a hint that it's known to be likely. + * + * May be called with @task->mm->mmap_sem held for writing. + */ +static inline int tracehook_expect_breakpoints(struct task_struct *task) +{ + return (task_ptrace(task) & PT_PTRACED) != 0; +} + /** * tracehook_unsafe_exec - check for exec declared unsafe due to tracing * @task: current task doing exec diff --git a/mm/nommu.c b/mm/nommu.c index 4462b6a3fcb9..5edccd9c9218 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -745,7 +745,7 @@ static unsigned long determine_vm_flags(struct file *file, * it's being traced - otherwise breakpoints set in it may interfere * with another untraced process */ - if ((flags & MAP_PRIVATE) && (current->ptrace & PT_PTRACED)) + if ((flags & MAP_PRIVATE) && tracehook_expect_breakpoints(current)) vm_flags &= ~VM_MAYSHARE; return vm_flags; -- cgit From c45aea27617d6a1e0aacddc3b0233f704222fcbd Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:50 -0700 Subject: tracehook: tracehook_signal_handler This defines tracehook_signal_handler() as a hook for the arch signal handling code to call. It gives ptrace the opportunity to stop for a pseudo-single-step trap immediately after signal handler setup is done. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index e113e09b0341..2d1426f8e33b 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -289,4 +289,27 @@ static inline void tracehook_finish_release_task(struct task_struct *task) ptrace_release_task(task); } +/** + * tracehook_signal_handler - signal handler setup is complete + * @sig: number of signal being delivered + * @info: siginfo_t of signal being delivered + * @ka: sigaction setting that chose the handler + * @regs: user register state + * @stepping: nonzero if debugger single-step or block-step in use + * + * Called by the arch code after a signal handler has been set up. + * Register and stack state reflects the user handler about to run. + * Signal mask changes have already been made. + * + * Called without locks, shortly before returning to user mode + * (or handling more signals). + */ +static inline void tracehook_signal_handler(int sig, siginfo_t *info, + const struct k_sigaction *ka, + struct pt_regs *regs, int stepping) +{ + if (stepping) + ptrace_notify(SIGTRAP); +} + #endif /* */ -- cgit From 35de254dc60f91004b3b5ebb1fc7b2c3093d6032 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:51 -0700 Subject: tracehook: tracehook_consider_ignored_signal This defines tracehook_consider_ignored_signal() has a fine-grained hook for deciding to prevent the normal short-circuit of sending an ignored signal, as ptrace does. There is no change, only cleanup. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 19 +++++++++++++++++++ kernel/signal.c | 27 ++++++++++++++++----------- 2 files changed, 35 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 2d1426f8e33b..8cffd34f88d5 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -312,4 +312,23 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info, ptrace_notify(SIGTRAP); } +/** + * tracehook_consider_ignored_signal - suppress short-circuit of ignored signal + * @task: task receiving the signal + * @sig: signal number being sent + * @handler: %SIG_IGN or %SIG_DFL + * + * Return zero iff tracing doesn't care to examine this ignored signal, + * so it can short-circuit normal delivery and never even get queued. + * Either @handler is %SIG_DFL and @sig's default is ignore, or it's %SIG_IGN. + * + * Called with @task->sighand->siglock held. + */ +static inline int tracehook_consider_ignored_signal(struct task_struct *task, + int sig, + void __user *handler) +{ + return (task_ptrace(task) & PT_PTRACED) != 0; +} + #endif /* */ diff --git a/kernel/signal.c b/kernel/signal.c index 8715c18b27b9..9efd1cee6d0b 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -39,24 +40,21 @@ static struct kmem_cache *sigqueue_cachep; -static int __sig_ignored(struct task_struct *t, int sig) +static void __user *sig_handler(struct task_struct *t, int sig) { - void __user *handler; + return t->sighand->action[sig - 1].sa.sa_handler; +} +static int sig_handler_ignored(void __user *handler, int sig) +{ /* Is it explicitly or implicitly ignored? */ - - handler = t->sighand->action[sig - 1].sa.sa_handler; return handler == SIG_IGN || (handler == SIG_DFL && sig_kernel_ignore(sig)); } static int sig_ignored(struct task_struct *t, int sig) { - /* - * Tracers always want to know about signals.. - */ - if (t->ptrace & PT_PTRACED) - return 0; + void __user *handler; /* * Blocked signals are never ignored, since the @@ -66,7 +64,14 @@ static int sig_ignored(struct task_struct *t, int sig) if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig)) return 0; - return __sig_ignored(t, sig); + handler = sig_handler(t, sig); + if (!sig_handler_ignored(handler, sig)) + return 0; + + /* + * Tracers may want to know about even ignored signals. + */ + return !tracehook_consider_ignored_signal(t, sig, handler); } /* @@ -2298,7 +2303,7 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact) * (for example, SIGCHLD), shall cause the pending signal to * be discarded, whether or not it is blocked" */ - if (__sig_ignored(t, sig)) { + if (sig_handler_ignored(sig_handler(t, sig), sig)) { sigemptyset(&mask); sigaddset(&mask, sig); rm_from_queue_full(&mask, &t->signal->shared_pending); -- cgit From 445a91d2fe3667fb8fc251433645f686933cf56a Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:52 -0700 Subject: tracehook: tracehook_consider_fatal_signal This defines tracehook_consider_fatal_signal() has a fine-grained hook for deciding to skip the special cases for a fatal signal, as ptrace does. There is no change, only cleanup. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 21 +++++++++++++++++++++ kernel/signal.c | 9 +++++---- 2 files changed, 26 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 8cffd34f88d5..8b4c15e208fe 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -331,4 +331,25 @@ static inline int tracehook_consider_ignored_signal(struct task_struct *task, return (task_ptrace(task) & PT_PTRACED) != 0; } +/** + * tracehook_consider_fatal_signal - suppress special handling of fatal signal + * @task: task receiving the signal + * @sig: signal number being sent + * @handler: %SIG_DFL or %SIG_IGN + * + * Return nonzero to prevent special handling of this termination signal. + * Normally @handler is %SIG_DFL. It can be %SIG_IGN if @sig is ignored, + * in which case force_sig() is about to reset it to %SIG_DFL. + * When this returns zero, this signal might cause a quick termination + * that does not give the debugger a chance to intercept the signal. + * + * Called with or without @task->sighand->siglock held. + */ +static inline int tracehook_consider_fatal_signal(struct task_struct *task, + int sig, + void __user *handler) +{ + return (task_ptrace(task) & PT_PTRACED) != 0; +} + #endif /* */ diff --git a/kernel/signal.c b/kernel/signal.c index 9efd1cee6d0b..1a942ce32ba0 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -300,12 +300,12 @@ flush_signal_handlers(struct task_struct *t, int force_default) int unhandled_signal(struct task_struct *tsk, int sig) { + void __user *handler = tsk->sighand->action[sig-1].sa.sa_handler; if (is_global_init(tsk)) return 1; - if (tsk->ptrace & PT_PTRACED) + if (handler != SIG_IGN && handler != SIG_DFL) return 0; - return (tsk->sighand->action[sig-1].sa.sa_handler == SIG_IGN) || - (tsk->sighand->action[sig-1].sa.sa_handler == SIG_DFL); + return !tracehook_consider_fatal_signal(tsk, sig, handler); } @@ -761,7 +761,8 @@ static void complete_signal(int sig, struct task_struct *p, int group) if (sig_fatal(p, sig) && !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) && !sigismember(&t->real_blocked, sig) && - (sig == SIGKILL || !(t->ptrace & PT_PTRACED))) { + (sig == SIGKILL || + !tracehook_consider_fatal_signal(t, sig, SIG_DFL))) { /* * This signal will be fatal to the whole group. */ -- cgit From 283d7559e7712f95a05331eb0a85394c6368101b Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:52 -0700 Subject: tracehook: syscall This adds standard tracehook.h inlines for arch code to call when TIF_SYSCALL_TRACE has been set. This replaces having each arch implement the ptrace guts for its syscall tracing support. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 8b4c15e208fe..3548694a24db 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -66,6 +66,76 @@ static inline int tracehook_expect_breakpoints(struct task_struct *task) return (task_ptrace(task) & PT_PTRACED) != 0; } +/* + * ptrace report for syscall entry and exit looks identical. + */ +static inline void ptrace_report_syscall(struct pt_regs *regs) +{ + int ptrace = task_ptrace(current); + + if (!(ptrace & PT_PTRACED)) + return; + + ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); + + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + +/** + * tracehook_report_syscall_entry - task is about to attempt a system call + * @regs: user register state of current task + * + * This will be called if %TIF_SYSCALL_TRACE has been set, when the + * current task has just entered the kernel for a system call. + * Full user register state is available here. Changing the values + * in @regs can affect the system call number and arguments to be tried. + * It is safe to block here, preventing the system call from beginning. + * + * Returns zero normally, or nonzero if the calling arch code should abort + * the system call. That must prevent normal entry so no system call is + * made. If @task ever returns to user mode after this, its register state + * is unspecified, but should be something harmless like an %ENOSYS error + * return. + * + * Called without locks, just after entering kernel mode. + */ +static inline __must_check int tracehook_report_syscall_entry( + struct pt_regs *regs) +{ + ptrace_report_syscall(regs); + return 0; +} + +/** + * tracehook_report_syscall_exit - task has just finished a system call + * @regs: user register state of current task + * @step: nonzero if simulating single-step or block-step + * + * This will be called if %TIF_SYSCALL_TRACE has been set, when the + * current task has just finished an attempted system call. Full + * user register state is available here. It is safe to block here, + * preventing signals from being processed. + * + * If @step is nonzero, this report is also in lieu of the normal + * trap that would follow the system call instruction because + * user_enable_block_step() or user_enable_single_step() was used. + * In this case, %TIF_SYSCALL_TRACE might not be set. + * + * Called without locks, just before checking for pending signals. + */ +static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step) +{ + ptrace_report_syscall(regs); +} + /** * tracehook_unsafe_exec - check for exec declared unsafe due to tracing * @task: current task doing exec -- cgit From 7bcf6a2ca5f639b038c48711ebe6c4eca2036641 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:53 -0700 Subject: tracehook: get_signal_to_deliver This defines the tracehook_get_signal() hook to allow tracing code to slip in before normal signal dequeuing. This lays the groundwork for new tracing features that can inject synthetic signals outside the normal queue or control the disposition of delivered signals. The calling convention lets tracehook_get_signal() decide both exactly what will happen and what signal number to report in the handler/exit. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 29 +++++++++++++++++++++++++++++ kernel/signal.c | 38 +++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 3548694a24db..42a0d7b11959 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -422,4 +422,33 @@ static inline int tracehook_consider_fatal_signal(struct task_struct *task, return (task_ptrace(task) & PT_PTRACED) != 0; } +/** + * tracehook_get_signal - deliver synthetic signal to traced task + * @task: @current + * @regs: task_pt_regs(@current) + * @info: details of synthetic signal + * @return_ka: sigaction for synthetic signal + * + * Return zero to check for a real pending signal normally. + * Return -1 after releasing the siglock to repeat the check. + * Return a signal number to induce an artifical signal delivery, + * setting *@info and *@return_ka to specify its details and behavior. + * + * The @return_ka->sa_handler value controls the disposition of the + * signal, no matter the signal number. For %SIG_DFL, the return value + * is a representative signal to indicate the behavior (e.g. %SIGTERM + * for death, %SIGQUIT for core dump, %SIGSTOP for job control stop, + * %SIGTSTP for stop unless in an orphaned pgrp), but the signal number + * reported will be @info->si_signo instead. + * + * Called with @task->sighand->siglock held, before dequeuing pending signals. + */ +static inline int tracehook_get_signal(struct task_struct *task, + struct pt_regs *regs, + siginfo_t *info, + struct k_sigaction *return_ka) +{ + return 0; +} + #endif /* */ diff --git a/kernel/signal.c b/kernel/signal.c index 1a942ce32ba0..10b31ecdd9c8 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1754,17 +1754,33 @@ relock: do_signal_stop(0)) goto relock; - signr = dequeue_signal(current, ¤t->blocked, info); - if (!signr) - break; /* will return 0 */ + /* + * Tracing can induce an artifical signal and choose sigaction. + * The return value in @signr determines the default action, + * but @info->si_signo is the signal number we will report. + */ + signr = tracehook_get_signal(current, regs, info, return_ka); + if (unlikely(signr < 0)) + goto relock; + if (unlikely(signr != 0)) + ka = return_ka; + else { + signr = dequeue_signal(current, ¤t->blocked, + info); - if (signr != SIGKILL) { - signr = ptrace_signal(signr, info, regs, cookie); if (!signr) - continue; + break; /* will return 0 */ + + if (signr != SIGKILL) { + signr = ptrace_signal(signr, info, + regs, cookie); + if (!signr) + continue; + } + + ka = &sighand->action[signr-1]; } - ka = &sighand->action[signr-1]; if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */ continue; if (ka->sa.sa_handler != SIG_DFL) { @@ -1812,7 +1828,7 @@ relock: spin_lock_irq(&sighand->siglock); } - if (likely(do_signal_stop(signr))) { + if (likely(do_signal_stop(info->si_signo))) { /* It released the siglock. */ goto relock; } @@ -1833,7 +1849,7 @@ relock: if (sig_kernel_coredump(signr)) { if (print_fatal_signals) - print_fatal_signal(regs, signr); + print_fatal_signal(regs, info->si_signo); /* * If it was able to dump core, this kills all * other threads in the group and synchronizes with @@ -1842,13 +1858,13 @@ relock: * first and our do_group_exit call below will use * that value and ignore the one we pass it. */ - do_coredump((long)signr, signr, regs); + do_coredump(info->si_signo, info->si_signo, regs); } /* * Death signals, no core dump. */ - do_group_exit(signr); + do_group_exit(info->si_signo); /* NOTREACHED */ } spin_unlock_irq(&sighand->siglock); -- cgit From fa00b80b3c41a845b3d56f866fb40a2e98754c51 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:54 -0700 Subject: tracehook: job control This defines the tracehook_notify_jctl() hook to formalize the ptrace effects on the job control notifications. There is no change, only cleanup. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 20 ++++++++++++++++++++ kernel/signal.c | 10 +++++----- 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 42a0d7b11959..6dc428dd2f38 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -451,4 +451,24 @@ static inline int tracehook_get_signal(struct task_struct *task, return 0; } +/** + * tracehook_notify_jctl - report about job control stop/continue + * @notify: nonzero if this is the last thread in the group to stop + * @why: %CLD_STOPPED or %CLD_CONTINUED + * + * This is called when we might call do_notify_parent_cldstop(). + * It's called when about to stop for job control; we are already in + * %TASK_STOPPED state, about to call schedule(). It's also called when + * a delayed %CLD_STOPPED or %CLD_CONTINUED report is ready to be made. + * + * Return nonzero to generate a %SIGCHLD with @why, which is + * normal if @notify is nonzero. + * + * Called with no locks held. + */ +static inline int tracehook_notify_jctl(int notify, int why) +{ + return notify || (current->ptrace & PT_PTRACED); +} + #endif /* */ diff --git a/kernel/signal.c b/kernel/signal.c index 10b31ecdd9c8..e9e699f4b1bd 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -596,9 +596,6 @@ static int check_kill_permission(int sig, struct siginfo *info, return security_task_kill(t, info, sig, 0); } -/* forward decl */ -static void do_notify_parent_cldstop(struct task_struct *tsk, int why); - /* * Handle magic process-wide effects of stop/continue signals. Unlike * the signal actions, these happen immediately at signal-generation @@ -1605,7 +1602,7 @@ finish_stop(int stop_count) * a group stop in progress and we are the last to stop, * report to the parent. When ptraced, every thread reports itself. */ - if (stop_count == 0 || (current->ptrace & PT_PTRACED)) { + if (tracehook_notify_jctl(stop_count == 0, CLD_STOPPED)) { read_lock(&tasklist_lock); do_notify_parent_cldstop(current, CLD_STOPPED); read_unlock(&tasklist_lock); @@ -1741,6 +1738,9 @@ relock: signal->flags &= ~SIGNAL_CLD_MASK; spin_unlock_irq(&sighand->siglock); + if (unlikely(!tracehook_notify_jctl(1, why))) + goto relock; + read_lock(&tasklist_lock); do_notify_parent_cldstop(current->group_leader, why); read_unlock(&tasklist_lock); @@ -1906,7 +1906,7 @@ void exit_signals(struct task_struct *tsk) out: spin_unlock_irq(&tsk->sighand->siglock); - if (unlikely(group_stop)) { + if (unlikely(group_stop) && tracehook_notify_jctl(1, CLD_STOPPED)) { read_lock(&tasklist_lock); do_notify_parent_cldstop(tsk, CLD_STOPPED); read_unlock(&tasklist_lock); -- cgit From 2b2a1ff64afbadac842bbc58c5166962cf4f7664 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:54 -0700 Subject: tracehook: death This moves the ptrace logic in task death (exit_notify) into tracehook.h inlines. Some code is rearranged slightly to make things nicer. There is no change, only cleanup. There is one hook called with the tasklist_lock write-locked, as ptrace needs. There is also a new hook called after exit_state changes and without locks. This is a better place for tracing work to be in the future, since it doesn't delay the whole system with locking. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 2 +- include/linux/tracehook.h | 52 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/exit.c | 26 ++++++++---------------- kernel/signal.c | 10 ++++++--- 4 files changed, 69 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index adb8077dc463..a95d84d0da95 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1796,7 +1796,7 @@ extern int kill_pid_info_as_uid(int, struct siginfo *, struct pid *, uid_t, uid_ extern int kill_pgrp(struct pid *pid, int sig, int priv); extern int kill_pid(struct pid *pid, int sig, int priv); extern int kill_proc_info(int, struct siginfo *, pid_t); -extern void do_notify_parent(struct task_struct *, int); +extern int do_notify_parent(struct task_struct *, int); extern void force_sig(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *); extern int send_sig(int, struct task_struct *, int); diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 6dc428dd2f38..4c50e1b57349 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -471,4 +471,56 @@ static inline int tracehook_notify_jctl(int notify, int why) return notify || (current->ptrace & PT_PTRACED); } +/** + * tracehook_notify_death - task is dead, ready to notify parent + * @task: @current task now exiting + * @death_cookie: value to pass to tracehook_report_death() + * @group_dead: nonzero if this was the last thread in the group to die + * + * Return the signal number to send our parent with do_notify_parent(), or + * zero to send no signal and leave a zombie, or -1 to self-reap right now. + * + * Called with write_lock_irq(&tasklist_lock) held. + */ +static inline int tracehook_notify_death(struct task_struct *task, + void **death_cookie, int group_dead) +{ + if (task->exit_signal == -1) + return task->ptrace ? SIGCHLD : -1; + + /* + * If something other than our normal parent is ptracing us, then + * send it a SIGCHLD instead of honoring exit_signal. exit_signal + * only has special meaning to our real parent. + */ + if (thread_group_empty(task) && !ptrace_reparented(task)) + return task->exit_signal; + + return task->ptrace ? SIGCHLD : 0; +} + +/** + * tracehook_report_death - task is dead and ready to be reaped + * @task: @current task now exiting + * @signal: signal number sent to parent, or 0 or -1 + * @death_cookie: value passed back from tracehook_notify_death() + * @group_dead: nonzero if this was the last thread in the group to die + * + * Thread has just become a zombie or is about to self-reap. If positive, + * @signal is the signal number just sent to the parent (usually %SIGCHLD). + * If @signal is -1, this thread will self-reap. If @signal is 0, this is + * a delayed_group_leader() zombie. The @death_cookie was passed back by + * tracehook_notify_death(). + * + * If normal reaping is not inhibited, @task->exit_state might be changing + * in parallel. + * + * Called without locks. + */ +static inline void tracehook_report_death(struct task_struct *task, + int signal, void *death_cookie, + int group_dead) +{ +} + #endif /* */ diff --git a/kernel/exit.c b/kernel/exit.c index da28745f7c38..6cdf60712bd2 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -885,7 +885,8 @@ static void forget_original_parent(struct task_struct *father) */ static void exit_notify(struct task_struct *tsk, int group_dead) { - int state; + int signal; + void *cookie; /* * This does two things: @@ -922,22 +923,11 @@ static void exit_notify(struct task_struct *tsk, int group_dead) !capable(CAP_KILL)) tsk->exit_signal = SIGCHLD; - /* If something other than our normal parent is ptracing us, then - * send it a SIGCHLD instead of honoring exit_signal. exit_signal - * only has special meaning to our real parent. - */ - if (!task_detached(tsk) && thread_group_empty(tsk)) { - int signal = ptrace_reparented(tsk) ? - SIGCHLD : tsk->exit_signal; - do_notify_parent(tsk, signal); - } else if (tsk->ptrace) { - do_notify_parent(tsk, SIGCHLD); - } + signal = tracehook_notify_death(tsk, &cookie, group_dead); + if (signal > 0) + signal = do_notify_parent(tsk, signal); - state = EXIT_ZOMBIE; - if (task_detached(tsk) && likely(!tsk->ptrace)) - state = EXIT_DEAD; - tsk->exit_state = state; + tsk->exit_state = signal < 0 ? EXIT_DEAD : EXIT_ZOMBIE; /* mt-exec, de_thread() is waiting for us */ if (thread_group_leader(tsk) && @@ -947,8 +937,10 @@ static void exit_notify(struct task_struct *tsk, int group_dead) write_unlock_irq(&tasklist_lock); + tracehook_report_death(tsk, signal, cookie, group_dead); + /* If the process is dead, release it - nobody will wait for it */ - if (state == EXIT_DEAD) + if (signal < 0) release_task(tsk); } diff --git a/kernel/signal.c b/kernel/signal.c index e9e699f4b1bd..0e862d3130ff 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1326,9 +1326,11 @@ static inline void __wake_up_parent(struct task_struct *p, /* * Let a parent know about the death of a child. * For a stopped/continued status change, use do_notify_parent_cldstop instead. + * + * Returns -1 if our parent ignored us and so we've switched to + * self-reaping, or else @sig. */ - -void do_notify_parent(struct task_struct *tsk, int sig) +int do_notify_parent(struct task_struct *tsk, int sig) { struct siginfo info; unsigned long flags; @@ -1399,12 +1401,14 @@ void do_notify_parent(struct task_struct *tsk, int sig) */ tsk->exit_signal = -1; if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) - sig = 0; + sig = -1; } if (valid_signal(sig) && sig > 0) __group_send_sig_info(sig, &info, tsk->parent); __wake_up_parent(tsk, tsk->parent); spin_unlock_irqrestore(&psig->siglock, flags); + + return sig; } static void do_notify_parent_cldstop(struct task_struct *tsk, int why) -- cgit From b787f7ba677840da16a2228c16571ce8a1fcb799 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:55 -0700 Subject: tracehook: force signal_pending() This defines a new hook tracehook_force_sigpending() that lets tracing code decide to force TIF_SIGPENDING on in recalc_sigpending(). This is not used yet, so it compiles away to nothing for now. It lays the groundwork for new tracing code that can interrupt a task synthetically without actually sending a signal. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 14 ++++++++++++++ kernel/signal.c | 4 +++- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 4c50e1b57349..43bc51b6bd33 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -422,6 +422,20 @@ static inline int tracehook_consider_fatal_signal(struct task_struct *task, return (task_ptrace(task) & PT_PTRACED) != 0; } +/** + * tracehook_force_sigpending - let tracing force signal_pending(current) on + * + * Called when recomputing our signal_pending() flag. Return nonzero + * to force the signal_pending() flag on, so that tracehook_get_signal() + * will be called before the next return to user mode. + * + * Called with @current->sighand->siglock held. + */ +static inline int tracehook_force_sigpending(void) +{ + return 0; +} + /** * tracehook_get_signal - deliver synthetic signal to traced task * @task: @current diff --git a/kernel/signal.c b/kernel/signal.c index 0e862d3130ff..954f77d7e3bc 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -134,7 +134,9 @@ void recalc_sigpending_and_wake(struct task_struct *t) void recalc_sigpending(void) { - if (!recalc_sigpending_tsk(current) && !freezing(current)) + if (unlikely(tracehook_force_sigpending())) + set_thread_flag(TIF_SIGPENDING); + else if (!recalc_sigpending_tsk(current) && !freezing(current)) clear_thread_flag(TIF_SIGPENDING); } -- cgit From 64b1208d5b0ef8859fd52ea7ae286a3eb994669b Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:56 -0700 Subject: tracehook: TIF_NOTIFY_RESUME This adds tracehook.h inlines to enable a new arch feature in support of user debugging/tracing. This is not used yet, but it lays the groundwork for a debugger to be able to wrangle a task that's possibly running, without interrupting its syscalls in progress. Each arch should define TIF_NOTIFY_RESUME, and in their entry.S code treat it much like TIF_SIGPENDING. That is, it causes you to take the slow path when returning to user mode, where you get the full user-mode state accessible as for signal handling or ptrace. The arch code should check TIF_NOTIFY_RESUME after handling TIF_SIGPENDING. When it's set, clear it and then call tracehook_notify_resume(). In future, tracing code will call set_notify_resume() when it wants to get a callback in tracehook_notify_resume(). Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/tracehook.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 43bc51b6bd33..32867ab86c70 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -537,4 +537,38 @@ static inline void tracehook_report_death(struct task_struct *task, { } +#ifdef TIF_NOTIFY_RESUME +/** + * set_notify_resume - cause tracehook_notify_resume() to be called + * @task: task that will call tracehook_notify_resume() + * + * Calling this arranges that @task will call tracehook_notify_resume() + * before returning to user mode. If it's already running in user mode, + * it will enter the kernel and call tracehook_notify_resume() soon. + * If it's blocked, it will not be woken. + */ +static inline void set_notify_resume(struct task_struct *task) +{ + if (!test_and_set_tsk_thread_flag(task, TIF_NOTIFY_RESUME)) + kick_process(task); +} + +/** + * tracehook_notify_resume - report when about to return to user mode + * @regs: user-mode registers of @current task + * + * This is called when %TIF_NOTIFY_RESUME has been set. Now we are + * about to return to user mode, and the user state in @regs can be + * inspected or adjusted. The caller in arch code has cleared + * %TIF_NOTIFY_RESUME before the call. If the flag gets set again + * asynchronously, this will be called again before we return to + * user mode. + * + * Called without locks. + */ +static inline void tracehook_notify_resume(struct pt_regs *regs) +{ +} +#endif /* TIF_NOTIFY_RESUME */ + #endif /* */ -- cgit From 828c365cc8b8d38c346fccb19fa80d28f2240831 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:57 -0700 Subject: tracehook: asm/syscall.h This adds asm-generic/syscall.h, which documents what a real asm-ARCH/syscall.h file should define. This is not used yet, but will provide all the machine-dependent details of examining a user system call about to begin, in progress, or just ended. Each arch should add an asm-ARCH/syscall.h that defines all the entry points documented in asm-generic/syscall.h, as short inlines if possible. This lets us write new tracing code that understands user system call registers, without any new arch-specific work. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/syscall.h | 141 ++++++++++++++++++++++++++++++++++++++++++ include/linux/tracehook.h | 3 +- 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 include/asm-generic/syscall.h (limited to 'include/linux') diff --git a/include/asm-generic/syscall.h b/include/asm-generic/syscall.h new file mode 100644 index 000000000000..abcf34c2fdc7 --- /dev/null +++ b/include/asm-generic/syscall.h @@ -0,0 +1,141 @@ +/* + * Access to user system call parameters and results + * + * Copyright (C) 2008 Red Hat, Inc. All rights reserved. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * This file is a stub providing documentation for what functions + * asm-ARCH/syscall.h files need to define. Most arch definitions + * will be simple inlines. + * + * All of these functions expect to be called with no locks, + * and only when the caller is sure that the task of interest + * cannot return to user mode while we are looking at it. + */ + +#ifndef _ASM_SYSCALL_H +#define _ASM_SYSCALL_H 1 + +struct task_struct; +struct pt_regs; + +/** + * syscall_get_nr - find what system call a task is executing + * @task: task of interest, must be blocked + * @regs: task_pt_regs() of @task + * + * If @task is executing a system call or is at system call + * tracing about to attempt one, returns the system call number. + * If @task is not executing a system call, i.e. it's blocked + * inside the kernel for a fault or signal, returns -1. + * + * It's only valid to call this when @task is known to be blocked. + */ +long syscall_get_nr(struct task_struct *task, struct pt_regs *regs); + +/** + * syscall_rollback - roll back registers after an aborted system call + * @task: task of interest, must be in system call exit tracing + * @regs: task_pt_regs() of @task + * + * It's only valid to call this when @task is stopped for system + * call exit tracing (due to TIF_SYSCALL_TRACE or TIF_SYSCALL_AUDIT), + * after tracehook_report_syscall_entry() returned nonzero to prevent + * the system call from taking place. + * + * This rolls back the register state in @regs so it's as if the + * system call instruction was a no-op. The registers containing + * the system call number and arguments are as they were before the + * system call instruction. This may not be the same as what the + * register state looked like at system call entry tracing. + */ +void syscall_rollback(struct task_struct *task, struct pt_regs *regs); + +/** + * syscall_get_error - check result of traced system call + * @task: task of interest, must be blocked + * @regs: task_pt_regs() of @task + * + * Returns 0 if the system call succeeded, or -ERRORCODE if it failed. + * + * It's only valid to call this when @task is stopped for tracing on exit + * from a system call, due to %TIF_SYSCALL_TRACE or %TIF_SYSCALL_AUDIT. + */ +long syscall_get_error(struct task_struct *task, struct pt_regs *regs); + +/** + * syscall_get_return_value - get the return value of a traced system call + * @task: task of interest, must be blocked + * @regs: task_pt_regs() of @task + * + * Returns the return value of the successful system call. + * This value is meaningless if syscall_get_error() returned nonzero. + * + * It's only valid to call this when @task is stopped for tracing on exit + * from a system call, due to %TIF_SYSCALL_TRACE or %TIF_SYSCALL_AUDIT. + */ +long syscall_get_return_value(struct task_struct *task, struct pt_regs *regs); + +/** + * syscall_set_return_value - change the return value of a traced system call + * @task: task of interest, must be blocked + * @regs: task_pt_regs() of @task + * @error: negative error code, or zero to indicate success + * @val: user return value if @error is zero + * + * This changes the results of the system call that user mode will see. + * If @error is zero, the user sees a successful system call with a + * return value of @val. If @error is nonzero, it's a negated errno + * code; the user sees a failed system call with this errno code. + * + * It's only valid to call this when @task is stopped for tracing on exit + * from a system call, due to %TIF_SYSCALL_TRACE or %TIF_SYSCALL_AUDIT. + */ +void syscall_set_return_value(struct task_struct *task, struct pt_regs *regs, + int error, long val); + +/** + * syscall_get_arguments - extract system call parameter values + * @task: task of interest, must be blocked + * @regs: task_pt_regs() of @task + * @i: argument index [0,5] + * @n: number of arguments; n+i must be [1,6]. + * @args: array filled with argument values + * + * Fetches @n arguments to the system call starting with the @i'th argument + * (from 0 through 5). Argument @i is stored in @args[0], and so on. + * An arch inline version is probably optimal when @i and @n are constants. + * + * It's only valid to call this when @task is stopped for tracing on + * entry to a system call, due to %TIF_SYSCALL_TRACE or %TIF_SYSCALL_AUDIT. + * It's invalid to call this with @i + @n > 6; we only support system calls + * taking up to 6 arguments. + */ +void syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, + unsigned int i, unsigned int n, unsigned long *args); + +/** + * syscall_set_arguments - change system call parameter value + * @task: task of interest, must be in system call entry tracing + * @regs: task_pt_regs() of @task + * @i: argument index [0,5] + * @n: number of arguments; n+i must be [1,6]. + * @args: array of argument values to store + * + * Changes @n arguments to the system call starting with the @i'th argument. + * @n'th argument to @val. Argument @i gets value @args[0], and so on. + * An arch inline version is probably optimal when @i and @n are constants. + * + * It's only valid to call this when @task is stopped for tracing on + * entry to a system call, due to %TIF_SYSCALL_TRACE or %TIF_SYSCALL_AUDIT. + * It's invalid to call this with @i + @n > 6; we only support system calls + * taking up to 6 arguments. + */ +void syscall_set_arguments(struct task_struct *task, struct pt_regs *regs, + unsigned int i, unsigned int n, + const unsigned long *args); + +#endif /* _ASM_SYSCALL_H */ diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 32867ab86c70..589f429619c9 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -103,7 +103,8 @@ static inline void ptrace_report_syscall(struct pt_regs *regs) * the system call. That must prevent normal entry so no system call is * made. If @task ever returns to user mode after this, its register state * is unspecified, but should be something harmless like an %ENOSYS error - * return. + * return. It should preserve enough information so that syscall_rollback() + * can work (see asm-generic/syscall.h). * * Called without locks, just after entering kernel mode. */ -- cgit From 85ba2d862e521375a8ee01526c5c46b1f24bb4af Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:58 -0700 Subject: tracehook: wait_task_inactive This extends wait_task_inactive() with a new argument so it can be used in a "soft" mode where it will check for the task changing state unexpectedly and back off. There is no change to existing callers. This lays the groundwork to allow robust, noninvasive tracing that can try to sample a blocked thread but back off safely if it wakes up. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/kernel/perfmon.c | 4 ++-- include/linux/sched.h | 8 ++++++-- kernel/kthread.c | 2 +- kernel/ptrace.c | 2 +- kernel/sched.c | 29 +++++++++++++++++++++++++++-- 5 files changed, 37 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 19d4493c6193..fc8f3509df27 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -2626,7 +2626,7 @@ pfm_task_incompatible(pfm_context_t *ctx, struct task_struct *task) /* * make sure the task is off any CPU */ - wait_task_inactive(task); + wait_task_inactive(task, 0); /* more to come... */ @@ -4774,7 +4774,7 @@ recheck: UNPROTECT_CTX(ctx, flags); - wait_task_inactive(task); + wait_task_inactive(task, 0); PROTECT_CTX(ctx, flags); diff --git a/include/linux/sched.h b/include/linux/sched.h index a95d84d0da95..f59318a0099b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1882,9 +1882,13 @@ extern void set_task_comm(struct task_struct *tsk, char *from); extern char *get_task_comm(char *to, struct task_struct *tsk); #ifdef CONFIG_SMP -extern void wait_task_inactive(struct task_struct * p); +extern unsigned long wait_task_inactive(struct task_struct *, long match_state); #else -#define wait_task_inactive(p) do { } while (0) +static inline unsigned long wait_task_inactive(struct task_struct *p, + long match_state) +{ + return 1; +} #endif #define next_task(p) list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks) diff --git a/kernel/kthread.c b/kernel/kthread.c index 6111c27491b1..96cff2f8710b 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -176,7 +176,7 @@ void kthread_bind(struct task_struct *k, unsigned int cpu) return; } /* Must have done schedule() in kthread() before we set_task_cpu */ - wait_task_inactive(k); + wait_task_inactive(k, 0); set_task_cpu(k, cpu); k->cpus_allowed = cpumask_of_cpu(cpu); k->rt.nr_cpus_allowed = 1; diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 8392a9da6450..082b3fcb32a0 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -107,7 +107,7 @@ int ptrace_check_attach(struct task_struct *child, int kill) read_unlock(&tasklist_lock); if (!ret && !kill) - wait_task_inactive(child); + ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH; /* All systems go.. */ return ret; diff --git a/kernel/sched.c b/kernel/sched.c index fde1a1026359..0236958addcb 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1867,16 +1867,24 @@ migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req) /* * wait_task_inactive - wait for a thread to unschedule. * + * If @match_state is nonzero, it's the @p->state value just checked and + * not expected to change. If it changes, i.e. @p might have woken up, + * then return zero. When we succeed in waiting for @p to be off its CPU, + * we return a positive number (its total switch count). If a second call + * a short while later returns the same number, the caller can be sure that + * @p has remained unscheduled the whole time. + * * The caller must ensure that the task *will* unschedule sometime soon, * else this function might spin for a *long* time. This function can't * be called with interrupts off, or it may introduce deadlock with * smp_call_function() if an IPI is sent by the same process we are * waiting to become inactive. */ -void wait_task_inactive(struct task_struct *p) +unsigned long wait_task_inactive(struct task_struct *p, long match_state) { unsigned long flags; int running, on_rq; + unsigned long ncsw; struct rq *rq; for (;;) { @@ -1899,8 +1907,11 @@ void wait_task_inactive(struct task_struct *p) * return false if the runqueue has changed and p * is actually now running somewhere else! */ - while (task_running(rq, p)) + while (task_running(rq, p)) { + if (match_state && unlikely(p->state != match_state)) + return 0; cpu_relax(); + } /* * Ok, time to look more closely! We need the rq @@ -1910,8 +1921,20 @@ void wait_task_inactive(struct task_struct *p) rq = task_rq_lock(p, &flags); running = task_running(rq, p); on_rq = p->se.on_rq; + ncsw = 0; + if (!match_state || p->state == match_state) { + ncsw = p->nivcsw + p->nvcsw; + if (unlikely(!ncsw)) + ncsw = 1; + } task_rq_unlock(rq, &flags); + /* + * If it changed from the expected state, bail out now. + */ + if (unlikely(!ncsw)) + break; + /* * Was it really running after all now that we * checked with the proper locks actually held? @@ -1944,6 +1967,8 @@ void wait_task_inactive(struct task_struct *p) */ break; } + + return ncsw; } /*** -- cgit From bbc698636ed48b6fcd323964e0f847a6a796325d Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 25 Jul 2008 19:45:59 -0700 Subject: task_current_syscall This adds the new function task_current_syscall() on machines where the asm/syscall.h interface is supported (CONFIG_HAVE_ARCH_TRACEHOOK). It's exported for modules to use in the future. This function safely samples the state of a blocked thread to collect what system call it is blocked in, and the six system call argument registers. Signed-off-by: Roland McGrath Cc: Oleg Nesterov Reviewed-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/ptrace.h | 4 +++ lib/Makefile | 2 ++ lib/syscall.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 lib/syscall.c (limited to 'include/linux') diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index ed69c03692d9..fd31756e1a00 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -314,6 +314,10 @@ static inline void user_enable_block_step(struct task_struct *task) #define arch_ptrace_stop(code, info) do { } while (0) #endif +extern int task_current_syscall(struct task_struct *target, long *callno, + unsigned long args[6], unsigned int maxargs, + unsigned long *sp, unsigned long *pc); + #endif #endif diff --git a/lib/Makefile b/lib/Makefile index 9085ad6fa53d..942c7250f603 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -78,6 +78,8 @@ lib-$(CONFIG_GENERIC_BUG) += bug.o obj-$(CONFIG_HAVE_LMB) += lmb.o +obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/syscall.c b/lib/syscall.c new file mode 100644 index 000000000000..a4f7067f72fa --- /dev/null +++ b/lib/syscall.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +static int collect_syscall(struct task_struct *target, long *callno, + unsigned long args[6], unsigned int maxargs, + unsigned long *sp, unsigned long *pc) +{ + struct pt_regs *regs = task_pt_regs(target); + if (unlikely(!regs)) + return -EAGAIN; + + *sp = user_stack_pointer(regs); + *pc = instruction_pointer(regs); + + *callno = syscall_get_nr(target, regs); + if (*callno != -1L && maxargs > 0) + syscall_get_arguments(target, regs, 0, maxargs, args); + + return 0; +} + +/** + * task_current_syscall - Discover what a blocked task is doing. + * @target: thread to examine + * @callno: filled with system call number or -1 + * @args: filled with @maxargs system call arguments + * @maxargs: number of elements in @args to fill + * @sp: filled with user stack pointer + * @pc: filled with user PC + * + * If @target is blocked in a system call, returns zero with *@callno + * set to the the call's number and @args filled in with its arguments. + * Registers not used for system call arguments may not be available and + * it is not kosher to use &struct user_regset calls while the system + * call is still in progress. Note we may get this result if @target + * has finished its system call but not yet returned to user mode, such + * as when it's stopped for signal handling or syscall exit tracing. + * + * If @target is blocked in the kernel during a fault or exception, + * returns zero with *@callno set to -1 and does not fill in @args. + * If so, it's now safe to examine @target using &struct user_regset + * get() calls as long as we're sure @target won't return to user mode. + * + * Returns -%EAGAIN if @target does not remain blocked. + * + * Returns -%EINVAL if @maxargs is too large (maximum is six). + */ +int task_current_syscall(struct task_struct *target, long *callno, + unsigned long args[6], unsigned int maxargs, + unsigned long *sp, unsigned long *pc) +{ + long state; + unsigned long ncsw; + + if (unlikely(maxargs > 6)) + return -EINVAL; + + if (target == current) + return collect_syscall(target, callno, args, maxargs, sp, pc); + + state = target->state; + if (unlikely(!state)) + return -EAGAIN; + + ncsw = wait_task_inactive(target, state); + if (unlikely(!ncsw) || + unlikely(collect_syscall(target, callno, args, maxargs, sp, pc)) || + unlikely(wait_task_inactive(target, state) != ncsw)) + return -EAGAIN; + + return 0; +} +EXPORT_SYMBOL_GPL(task_current_syscall); -- cgit From 9d8fddfb17aaee4ffc5e3d0560620d0fa8b50a42 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 19:46:23 -0700 Subject: mm/allocpercpu.c: make 4 functions static This patch makes the following needlessly global functions static: - percpu_depopulate() - __percpu_depopulate_mask() - percpu_populate() - __percpu_populate_mask() Signed-off-by: Adrian Bunk Acked-by: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/percpu.h | 29 ----------------------------- mm/allocpercpu.c | 20 +++++++++++--------- 2 files changed, 11 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 4cdd393e71e1..fac3337547eb 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -74,11 +74,6 @@ struct percpu_data { (__typeof__(ptr))__p->ptrs[(cpu)]; \ }) -extern void *percpu_populate(void *__pdata, size_t size, gfp_t gfp, int cpu); -extern void percpu_depopulate(void *__pdata, int cpu); -extern int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp, - cpumask_t *mask); -extern void __percpu_depopulate_mask(void *__pdata, cpumask_t *mask); extern void *__percpu_alloc_mask(size_t size, gfp_t gfp, cpumask_t *mask); extern void percpu_free(void *__pdata); @@ -86,26 +81,6 @@ extern void percpu_free(void *__pdata); #define percpu_ptr(ptr, cpu) ({ (void)(cpu); (ptr); }) -static inline void percpu_depopulate(void *__pdata, int cpu) -{ -} - -static inline void __percpu_depopulate_mask(void *__pdata, cpumask_t *mask) -{ -} - -static inline void *percpu_populate(void *__pdata, size_t size, gfp_t gfp, - int cpu) -{ - return percpu_ptr(__pdata, cpu); -} - -static inline int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp, - cpumask_t *mask) -{ - return 0; -} - static __always_inline void *__percpu_alloc_mask(size_t size, gfp_t gfp, cpumask_t *mask) { return kzalloc(size, gfp); @@ -118,10 +93,6 @@ static inline void percpu_free(void *__pdata) #endif /* CONFIG_SMP */ -#define percpu_populate_mask(__pdata, size, gfp, mask) \ - __percpu_populate_mask((__pdata), (size), (gfp), &(mask)) -#define percpu_depopulate_mask(__pdata, mask) \ - __percpu_depopulate_mask((__pdata), &(mask)) #define percpu_alloc_mask(size, gfp, mask) \ __percpu_alloc_mask((size), (gfp), &(mask)) diff --git a/mm/allocpercpu.c b/mm/allocpercpu.c index 843364594e23..4297bc41bfd2 100644 --- a/mm/allocpercpu.c +++ b/mm/allocpercpu.c @@ -18,27 +18,28 @@ * Depopulating per-cpu data for a cpu going offline would be a typical * use case. You need to register a cpu hotplug handler for that purpose. */ -void percpu_depopulate(void *__pdata, int cpu) +static void percpu_depopulate(void *__pdata, int cpu) { struct percpu_data *pdata = __percpu_disguise(__pdata); kfree(pdata->ptrs[cpu]); pdata->ptrs[cpu] = NULL; } -EXPORT_SYMBOL_GPL(percpu_depopulate); /** * percpu_depopulate_mask - depopulate per-cpu data for some cpu's * @__pdata: per-cpu data to depopulate * @mask: depopulate per-cpu data for cpu's selected through mask bits */ -void __percpu_depopulate_mask(void *__pdata, cpumask_t *mask) +static void __percpu_depopulate_mask(void *__pdata, cpumask_t *mask) { int cpu; for_each_cpu_mask_nr(cpu, *mask) percpu_depopulate(__pdata, cpu); } -EXPORT_SYMBOL_GPL(__percpu_depopulate_mask); + +#define percpu_depopulate_mask(__pdata, mask) \ + __percpu_depopulate_mask((__pdata), &(mask)) /** * percpu_populate - populate per-cpu data for given cpu @@ -51,7 +52,7 @@ EXPORT_SYMBOL_GPL(__percpu_depopulate_mask); * use case. You need to register a cpu hotplug handler for that purpose. * Per-cpu object is populated with zeroed buffer. */ -void *percpu_populate(void *__pdata, size_t size, gfp_t gfp, int cpu) +static void *percpu_populate(void *__pdata, size_t size, gfp_t gfp, int cpu) { struct percpu_data *pdata = __percpu_disguise(__pdata); int node = cpu_to_node(cpu); @@ -68,7 +69,6 @@ void *percpu_populate(void *__pdata, size_t size, gfp_t gfp, int cpu) pdata->ptrs[cpu] = kzalloc(size, gfp); return pdata->ptrs[cpu]; } -EXPORT_SYMBOL_GPL(percpu_populate); /** * percpu_populate_mask - populate per-cpu data for more cpu's @@ -79,8 +79,8 @@ EXPORT_SYMBOL_GPL(percpu_populate); * * Per-cpu objects are populated with zeroed buffers. */ -int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp, - cpumask_t *mask) +static int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp, + cpumask_t *mask) { cpumask_t populated; int cpu; @@ -94,7 +94,9 @@ int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp, cpu_set(cpu, populated); return 0; } -EXPORT_SYMBOL_GPL(__percpu_populate_mask); + +#define percpu_populate_mask(__pdata, size, gfp, mask) \ + __percpu_populate_mask((__pdata), (size), (gfp), &(mask)) /** * percpu_alloc_mask - initial setup of per-cpu data -- cgit From 15f59adae001766a2c7f7fe4f196387bb04bcff5 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 19:46:23 -0700 Subject: make mm/memory.c:print_bad_pte() static This patch makes the needlessly global print_bad_pte() static. Signed-off-by: Adrian Bunk Reviewed-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 1 - mm/memory.c | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index f3fd70d6029f..6e695eaab4ce 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -810,7 +810,6 @@ extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void * int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas); -void print_bad_pte(struct vm_area_struct *, pte_t, unsigned long); extern int try_to_release_page(struct page * page, gfp_t gfp_mask); extern void do_invalidatepage(struct page *page, unsigned long offset); diff --git a/mm/memory.c b/mm/memory.c index 262e3eb6601a..a8ca04faaea6 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -374,7 +374,8 @@ static inline void add_mm_rss(struct mm_struct *mm, int file_rss, int anon_rss) * * The calling function must still handle the error. */ -void print_bad_pte(struct vm_area_struct *vma, pte_t pte, unsigned long vaddr) +static void print_bad_pte(struct vm_area_struct *vma, pte_t pte, + unsigned long vaddr) { printk(KERN_ERR "Bad pte = %08llx, process = %s, " "vm_flags = %lx, vaddr = %lx\n", -- cgit From 7c363b8c6536f26934172d3c46f0bbec01a97c61 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 19:46:24 -0700 Subject: mm/swapfile.c: make code static This patch makes the following needlessly global code static: - swap_lock - nr_swapfiles - struct swap_list Signed-off-by: Adrian Bunk Reviewed-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 3 --- mm/swapfile.c | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/swap.h b/include/linux/swap.h index 0b3377650c85..de40f169a4e4 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -237,7 +237,6 @@ extern struct page *swapin_readahead(swp_entry_t, gfp_t, /* linux/mm/swapfile.c */ extern long total_swap_pages; -extern unsigned int nr_swapfiles; extern void si_swapinfo(struct sysinfo *); extern swp_entry_t get_swap_page(void); extern swp_entry_t get_swap_page_of_type(int); @@ -254,8 +253,6 @@ extern int can_share_swap_page(struct page *); extern int remove_exclusive_swap_page(struct page *); struct backing_dev_info; -extern spinlock_t swap_lock; - /* linux/mm/thrash.c */ extern struct mm_struct * swap_token_mm; extern void grab_swap_token(void); diff --git a/mm/swapfile.c b/mm/swapfile.c index af283933c14e..6beb6251e99d 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -33,8 +33,8 @@ #include #include -DEFINE_SPINLOCK(swap_lock); -unsigned int nr_swapfiles; +static DEFINE_SPINLOCK(swap_lock); +static unsigned int nr_swapfiles; long total_swap_pages; static int swap_overflow; static int least_priority; @@ -44,7 +44,7 @@ static const char Unused_file[] = "Unused swap file entry "; static const char Bad_offset[] = "Bad swap offset entry "; static const char Unused_offset[] = "Unused swap offset entry "; -struct swap_list_t swap_list = {-1, -1}; +static struct swap_list_t swap_list = {-1, -1}; static struct swap_info_struct swap_info[MAX_SWAPFILES]; -- cgit From 9580d85f9cdb076c4bfb467bc6c0d3c5e499957a Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 25 Jul 2008 19:46:25 -0700 Subject: drivers/char/rtc.c: make 2 functions static The following functions can now become static: - rtc_interrupt() - rtc_get_rtc_time() Signed-off-by: Adrian Bunk Acked-by: Bernhard Walle Acked-by: Paul Gortmaker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/rtc.c | 5 +++-- include/linux/rtc.h | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index dbefbb30ed44..d9799e2bcfbf 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -144,6 +144,7 @@ static ssize_t rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +static void rtc_get_rtc_time(struct rtc_time *rtc_tm); #ifdef RTC_IRQ static unsigned int rtc_poll(struct file *file, poll_table *wait); @@ -235,7 +236,7 @@ static inline unsigned char rtc_is_updating(void) * (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.) */ -irqreturn_t rtc_interrupt(int irq, void *dev_id) +static irqreturn_t rtc_interrupt(int irq, void *dev_id) { /* * Can be an alarm interrupt, update complete interrupt, @@ -1303,7 +1304,7 @@ static int rtc_proc_open(struct inode *inode, struct file *file) } #endif -void rtc_get_rtc_time(struct rtc_time *rtc_tm) +static void rtc_get_rtc_time(struct rtc_time *rtc_tm) { unsigned long uip_watchdog = jiffies, flags; unsigned char ctrl; diff --git a/include/linux/rtc.h b/include/linux/rtc.h index b01fe004cb5e..91f597ad6acc 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -225,8 +225,6 @@ typedef struct rtc_task { int rtc_register(rtc_task_t *task); int rtc_unregister(rtc_task_t *task); int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg); -void rtc_get_rtc_time(struct rtc_time *rtc_tm); -irqreturn_t rtc_interrupt(int irq, void *dev_id); #endif /* __KERNEL__ */ -- cgit From a9906a19193db69ad0158f289f839edf8aaf103f Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Sat, 26 Jul 2008 14:41:26 -0700 Subject: tracehook: comment fixes This fixes some typos and errors in comments. No code changes. Signed-off-by: Roland McGrath --- include/linux/tracehook.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index 589f429619c9..b1875582c1a1 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h @@ -244,7 +244,7 @@ static inline int tracehook_prepare_clone(unsigned clone_flags) * tracehook_finish_clone - new child created and being attached * @child: new child task * @clone_flags: %CLONE_* flags from clone/fork/vfork system call - * @trace: return value from tracehook_clone_prepare() + * @trace: return value from tracehook_prepare_clone() * * This is called immediately after adding @child to its parent's children list. * The @trace value is that returned by tracehook_prepare_clone(). @@ -259,19 +259,20 @@ static inline void tracehook_finish_clone(struct task_struct *child, /** * tracehook_report_clone - in parent, new child is about to start running - * @trace: return value from tracehook_clone_prepare() + * @trace: return value from tracehook_prepare_clone() * @regs: parent's user register state * @clone_flags: flags from parent's system call * @pid: new child's PID in the parent's namespace * @child: new child task * - * Called after a child is set up, but before it has been started running. - * The @trace value is that returned by tracehook_clone_prepare(). - * This is not a good place to block, because the child has not started yet. - * Suspend the child here if desired, and block in tracehook_clone_complete(). - * This must prevent the child from self-reaping if tracehook_clone_complete() - * uses the @child pointer; otherwise it might have died and been released by - * the time tracehook_report_clone_complete() is called. + * Called after a child is set up, but before it has been started + * running. @trace is the value returned by tracehook_prepare_clone(). + * This is not a good place to block, because the child has not started + * yet. Suspend the child here if desired, and then block in + * tracehook_report_clone_complete(). This must prevent the child from + * self-reaping if tracehook_report_clone_complete() uses the @child + * pointer; otherwise it might have died and been released by the time + * tracehook_report_report_clone_complete() is called. * * Called with no locks held, but the child cannot run until this returns. */ @@ -290,7 +291,7 @@ static inline void tracehook_report_clone(int trace, struct pt_regs *regs, /** * tracehook_report_clone_complete - new child is running - * @trace: return value from tracehook_clone_prepare() + * @trace: return value from tracehook_prepare_clone() * @regs: parent's user register state * @clone_flags: flags from parent's system call * @pid: new child's PID in the parent's namespace @@ -347,7 +348,7 @@ static inline void tracehook_prepare_release_task(struct task_struct *task) } /** - * tracehook_finish_release_task - task is being reaped, clean up tracing + * tracehook_finish_release_task - final tracing clean-up * @task: task in %EXIT_DEAD state * * This is called in release_task() when @task is being in the middle of -- cgit From 6edd8ee60ac9b974bd6ec3b1bcb2aab02762fa8c Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Thu, 24 Jul 2008 14:18:57 +0200 Subject: mmc: Export internal host state through debugfs When CONFIG_DEBUG_FS is set, create a few files under /sys/kernel/debug containing information about an mmc host's internal state. Currently, just a single file is created, "ios", which contains information about the current operating parameters for the bus (clock speed, bus width, etc.) Host drivers can add additional files and directories under the host's root directory by passing the debugfs_root field in struct mmc_host as the 'parent' parameter to debugfs_create_*. Signed-off-by: Haavard Skinnemoen Signed-off-by: Pierre Ossman --- drivers/mmc/core/Makefile | 1 + drivers/mmc/core/core.h | 4 ++ drivers/mmc/core/debugfs.c | 164 +++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/host.c | 8 +++ include/linux/mmc/host.h | 2 + 5 files changed, 179 insertions(+) create mode 100644 drivers/mmc/core/debugfs.c (limited to 'include/linux') diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 19a1a254a0c5..889e5f898f6f 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -12,3 +12,4 @@ mmc_core-y := core.o bus.o host.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o +mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index cdb332b7dedc..745da9881aa7 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -52,5 +52,9 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr); extern int use_spi_crc; +/* Debugfs information for hosts and cards */ +void mmc_add_host_debugfs(struct mmc_host *host); +void mmc_remove_host_debugfs(struct mmc_host *host); + #endif diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c new file mode 100644 index 000000000000..133c6e51f26b --- /dev/null +++ b/drivers/mmc/core/debugfs.c @@ -0,0 +1,164 @@ +/* + * Debugfs support for hosts and cards + * + * Copyright (C) 2008 Atmel Corporation + * + * 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 +#include +#include +#include + +#include + +#include "core.h" + +/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */ +static int mmc_ios_show(struct seq_file *s, void *data) +{ + static const char *vdd_str[] = { + [8] = "2.0", + [9] = "2.1", + [10] = "2.2", + [11] = "2.3", + [12] = "2.4", + [13] = "2.5", + [14] = "2.6", + [15] = "2.7", + [16] = "2.8", + [17] = "2.9", + [18] = "3.0", + [19] = "3.1", + [20] = "3.2", + [21] = "3.3", + [22] = "3.4", + [23] = "3.5", + [24] = "3.6", + }; + struct mmc_host *host = s->private; + struct mmc_ios *ios = &host->ios; + const char *str; + + seq_printf(s, "clock:\t\t%u Hz\n", ios->clock); + seq_printf(s, "vdd:\t\t%u ", ios->vdd); + if ((1 << ios->vdd) & MMC_VDD_165_195) + seq_printf(s, "(1.65 - 1.95 V)\n"); + else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1) + && vdd_str[ios->vdd] && vdd_str[ios->vdd + 1]) + seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd], + vdd_str[ios->vdd + 1]); + else + seq_printf(s, "(invalid)\n"); + + switch (ios->bus_mode) { + case MMC_BUSMODE_OPENDRAIN: + str = "open drain"; + break; + case MMC_BUSMODE_PUSHPULL: + str = "push-pull"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str); + + switch (ios->chip_select) { + case MMC_CS_DONTCARE: + str = "don't care"; + break; + case MMC_CS_HIGH: + str = "active high"; + break; + case MMC_CS_LOW: + str = "active low"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + str = "off"; + break; + case MMC_POWER_UP: + str = "up"; + break; + case MMC_POWER_ON: + str = "on"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str); + seq_printf(s, "bus width:\t%u (%u bits)\n", + ios->bus_width, 1 << ios->bus_width); + + switch (ios->timing) { + case MMC_TIMING_LEGACY: + str = "legacy"; + break; + case MMC_TIMING_MMC_HS: + str = "mmc high-speed"; + break; + case MMC_TIMING_SD_HS: + str = "sd high-speed"; + break; + default: + str = "invalid"; + break; + } + seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str); + + return 0; +} + +static int mmc_ios_open(struct inode *inode, struct file *file) +{ + return single_open(file, mmc_ios_show, inode->i_private); +} + +static const struct file_operations mmc_ios_fops = { + .open = mmc_ios_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void mmc_add_host_debugfs(struct mmc_host *host) +{ + struct dentry *root; + + root = debugfs_create_dir(mmc_hostname(host), NULL); + if (IS_ERR(root)) + /* Don't complain -- debugfs just isn't enabled */ + return; + if (!root) + /* Complain -- debugfs is enabled, but it failed to + * create the directory. */ + goto err_root; + + host->debugfs_root = root; + + if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops)) + goto err_ios; + + return; + +err_ios: + debugfs_remove_recursive(root); + host->debugfs_root = NULL; +err_root: + dev_err(&host->class_dev, "failed to initialize debugfs\n"); +} + +void mmc_remove_host_debugfs(struct mmc_host *host) +{ + debugfs_remove_recursive(host->debugfs_root); +} diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 1d795c5379b5..6da80fd4d974 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -127,6 +127,10 @@ int mmc_add_host(struct mmc_host *host) if (err) return err; +#ifdef CONFIG_DEBUG_FS + mmc_add_host_debugfs(host); +#endif + mmc_start_host(host); return 0; @@ -146,6 +150,10 @@ void mmc_remove_host(struct mmc_host *host) { mmc_stop_host(host); +#ifdef CONFIG_DEBUG_FS + mmc_remove_host_debugfs(host); +#endif + device_del(&host->class_dev); led_trigger_unregister_simple(host->led); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 10a2080086ca..9c288c909878 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -157,6 +157,8 @@ struct mmc_host { struct led_trigger *led; /* activity led */ #endif + struct dentry *debugfs_root; + unsigned long private[0] ____cacheline_aligned; }; -- cgit From f4b7f927b531ca350cfc4ca1bdc3377dac7f9a32 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Thu, 24 Jul 2008 14:18:58 +0200 Subject: mmc: Add per-card debugfs support For each card successfully added to the bus, create a subdirectory under the host's debugfs root with information about the card. At the moment, only a single file is added to the card directory for all cards: "state". It reflects the "state" field in struct mmc_card, indicating whether the card is present, readonly, etc. For MMC and SD cards (not SDIO), another file is added: "status". Reading this file will ask the card about its current status and return it. This can be useful if the card just refuses to respond to any commands, which might indicate that the card state is not what the MMC core thinks it is (due to a missing stop command, for example.) Signed-off-by: Haavard Skinnemoen Signed-off-by: Pierre Ossman --- drivers/mmc/core/bus.c | 8 ++++++ drivers/mmc/core/core.h | 3 +++ drivers/mmc/core/debugfs.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/card.h | 2 ++ 4 files changed, 74 insertions(+) (limited to 'include/linux') diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index fd95b18e988b..0d9b2d6f9ebf 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -252,6 +252,10 @@ int mmc_add_card(struct mmc_card *card) if (ret) return ret; +#ifdef CONFIG_DEBUG_FS + mmc_add_card_debugfs(card); +#endif + mmc_card_set_present(card); return 0; @@ -263,6 +267,10 @@ int mmc_add_card(struct mmc_card *card) */ void mmc_remove_card(struct mmc_card *card) { +#ifdef CONFIG_DEBUG_FS + mmc_remove_card_debugfs(card); +#endif + if (mmc_card_present(card)) { if (mmc_host_is_spi(card->host)) { printk(KERN_INFO "%s: SPI card removed\n", diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 745da9881aa7..c819effa1032 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -56,5 +56,8 @@ extern int use_spi_crc; void mmc_add_host_debugfs(struct mmc_host *host); void mmc_remove_host_debugfs(struct mmc_host *host); +void mmc_add_card_debugfs(struct mmc_card *card); +void mmc_remove_card_debugfs(struct mmc_card *card); + #endif diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 133c6e51f26b..1237bb4c722b 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -12,9 +12,11 @@ #include #include +#include #include #include "core.h" +#include "mmc_ops.h" /* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */ static int mmc_ios_show(struct seq_file *s, void *data) @@ -162,3 +164,62 @@ void mmc_remove_host_debugfs(struct mmc_host *host) { debugfs_remove_recursive(host->debugfs_root); } + +static int mmc_dbg_card_status_get(void *data, u64 *val) +{ + struct mmc_card *card = data; + u32 status; + int ret; + + mmc_claim_host(card->host); + + ret = mmc_send_status(data, &status); + if (!ret) + *val = status; + + mmc_release_host(card->host); + + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get, + NULL, "%08llx\n"); + +void mmc_add_card_debugfs(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + struct dentry *root; + + if (!host->debugfs_root) + return; + + root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root); + if (IS_ERR(root)) + /* Don't complain -- debugfs just isn't enabled */ + return; + if (!root) + /* Complain -- debugfs is enabled, but it failed to + * create the directory. */ + goto err; + + card->debugfs_root = root; + + if (!debugfs_create_x32("state", S_IRUSR, root, &card->state)) + goto err; + + if (mmc_card_mmc(card) || mmc_card_sd(card)) + if (!debugfs_create_file("status", S_IRUSR, root, card, + &mmc_dbg_card_status_fops)) + goto err; + + return; + +err: + debugfs_remove_recursive(root); + card->debugfs_root = NULL; + dev_err(&card->dev, "failed to initialize debugfs\n"); +} + +void mmc_remove_card_debugfs(struct mmc_card *card) +{ + debugfs_remove_recursive(card->debugfs_root); +} diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 0d508ac17d64..ee6e822d5994 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -111,6 +111,8 @@ struct mmc_card { unsigned num_info; /* number of info strings */ const char **info; /* info strings */ struct sdio_func_tuple *tuples; /* unknown common tuples */ + + struct dentry *debugfs_root; }; #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) -- cgit From 04578dd330f1ec6bc9c4233833bee0d0ca73ff09 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Sat, 26 Jul 2008 18:52:34 +0200 Subject: Define AF_ISDN and PF_ISDN Define the address and protocol family value for mISDN. Signed-off-by: Karsten Keil --- include/linux/socket.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/socket.h b/include/linux/socket.h index 950af631e7fb..dc5086fe7736 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -189,7 +189,8 @@ struct ucred { #define AF_BLUETOOTH 31 /* Bluetooth sockets */ #define AF_IUCV 32 /* IUCV sockets */ #define AF_RXRPC 33 /* RxRPC sockets */ -#define AF_MAX 34 /* For now.. */ +#define AF_ISDN 34 /* mISDN sockets */ +#define AF_MAX 35 /* For now.. */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -225,6 +226,7 @@ struct ucred { #define PF_BLUETOOTH AF_BLUETOOTH #define PF_IUCV AF_IUCV #define PF_RXRPC AF_RXRPC +#define PF_ISDN AF_ISDN #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ -- cgit From 1b2b03f8e514e4f68e293846ba511a948b80243c Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Sun, 27 Jul 2008 01:54:58 +0200 Subject: Add mISDN core files Add mISDN core files Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/Kconfig | 9 + drivers/isdn/mISDN/Makefile | 9 + drivers/isdn/mISDN/core.c | 244 +++++ drivers/isdn/mISDN/core.h | 77 ++ drivers/isdn/mISDN/fsm.c | 183 ++++ drivers/isdn/mISDN/fsm.h | 67 ++ drivers/isdn/mISDN/hwchannel.c | 365 +++++++ drivers/isdn/mISDN/layer1.c | 403 ++++++++ drivers/isdn/mISDN/layer1.h | 26 + drivers/isdn/mISDN/layer2.c | 2216 ++++++++++++++++++++++++++++++++++++++++ drivers/isdn/mISDN/layer2.h | 140 +++ drivers/isdn/mISDN/socket.c | 781 ++++++++++++++ drivers/isdn/mISDN/stack.c | 674 ++++++++++++ drivers/isdn/mISDN/tei.c | 1340 ++++++++++++++++++++++++ drivers/isdn/mISDN/timerdev.c | 301 ++++++ include/linux/mISDNhw.h | 193 ++++ include/linux/mISDNif.h | 487 +++++++++ 17 files changed, 7515 insertions(+) create mode 100644 drivers/isdn/mISDN/Kconfig create mode 100644 drivers/isdn/mISDN/Makefile create mode 100644 drivers/isdn/mISDN/core.c create mode 100644 drivers/isdn/mISDN/core.h create mode 100644 drivers/isdn/mISDN/fsm.c create mode 100644 drivers/isdn/mISDN/fsm.h create mode 100644 drivers/isdn/mISDN/hwchannel.c create mode 100644 drivers/isdn/mISDN/layer1.c create mode 100644 drivers/isdn/mISDN/layer1.h create mode 100644 drivers/isdn/mISDN/layer2.c create mode 100644 drivers/isdn/mISDN/layer2.h create mode 100644 drivers/isdn/mISDN/socket.c create mode 100644 drivers/isdn/mISDN/stack.c create mode 100644 drivers/isdn/mISDN/tei.c create mode 100644 drivers/isdn/mISDN/timerdev.c create mode 100644 include/linux/mISDNhw.h create mode 100644 include/linux/mISDNif.h (limited to 'include/linux') diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig new file mode 100644 index 000000000000..231bd0d08316 --- /dev/null +++ b/drivers/isdn/mISDN/Kconfig @@ -0,0 +1,9 @@ +# +# modularer ISDN driver +# + +menuconfig MISDN + tristate "Modular ISDN driver" + help + Enable support for the modular ISDN driver. + diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile new file mode 100644 index 000000000000..87c563d33612 --- /dev/null +++ b/drivers/isdn/mISDN/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the modular ISDN driver +# + +obj-$(CONFIG_MISDN) += mISDN_core.o + +# multi objects + +mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c new file mode 100644 index 000000000000..33068177b7c9 --- /dev/null +++ b/drivers/isdn/mISDN/core.c @@ -0,0 +1,244 @@ +/* + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include "core.h" + +static u_int debug; + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); + +static LIST_HEAD(devices); +DEFINE_RWLOCK(device_lock); +static u64 device_ids; +#define MAX_DEVICE_ID 63 + +static LIST_HEAD(Bprotocols); +DEFINE_RWLOCK(bp_lock); + +struct mISDNdevice +*get_mdevice(u_int id) +{ + struct mISDNdevice *dev; + + read_lock(&device_lock); + list_for_each_entry(dev, &devices, D.list) + if (dev->id == id) { + read_unlock(&device_lock); + return dev; + } + read_unlock(&device_lock); + return NULL; +} + +int +get_mdevice_count(void) +{ + struct mISDNdevice *dev; + int cnt = 0; + + read_lock(&device_lock); + list_for_each_entry(dev, &devices, D.list) + cnt++; + read_unlock(&device_lock); + return cnt; +} + +static int +get_free_devid(void) +{ + u_int i; + + for (i = 0; i <= MAX_DEVICE_ID; i++) + if (!test_and_set_bit(i, (u_long *)&device_ids)) + return i; + return -1; +} + +int +mISDN_register_device(struct mISDNdevice *dev, char *name) +{ + u_long flags; + int err; + + dev->id = get_free_devid(); + if (dev->id < 0) + return -EBUSY; + if (name && name[0]) + strcpy(dev->name, name); + else + sprintf(dev->name, "mISDN%d", dev->id); + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "mISDN_register %s %d\n", + dev->name, dev->id); + err = create_stack(dev); + if (err) + return err; + write_lock_irqsave(&device_lock, flags); + list_add_tail(&dev->D.list, &devices); + write_unlock_irqrestore(&device_lock, flags); + return 0; +} +EXPORT_SYMBOL(mISDN_register_device); + +void +mISDN_unregister_device(struct mISDNdevice *dev) { + u_long flags; + + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "mISDN_unregister %s %d\n", + dev->name, dev->id); + write_lock_irqsave(&device_lock, flags); + list_del(&dev->D.list); + write_unlock_irqrestore(&device_lock, flags); + test_and_clear_bit(dev->id, (u_long *)&device_ids); + delete_stack(dev); +} +EXPORT_SYMBOL(mISDN_unregister_device); + +u_int +get_all_Bprotocols(void) +{ + struct Bprotocol *bp; + u_int m = 0; + + read_lock(&bp_lock); + list_for_each_entry(bp, &Bprotocols, list) + m |= bp->Bprotocols; + read_unlock(&bp_lock); + return m; +} + +struct Bprotocol * +get_Bprotocol4mask(u_int m) +{ + struct Bprotocol *bp; + + read_lock(&bp_lock); + list_for_each_entry(bp, &Bprotocols, list) + if (bp->Bprotocols & m) { + read_unlock(&bp_lock); + return bp; + } + read_unlock(&bp_lock); + return NULL; +} + +struct Bprotocol * +get_Bprotocol4id(u_int id) +{ + u_int m; + + if (id < ISDN_P_B_START || id > 63) { + printk(KERN_WARNING "%s id not in range %d\n", + __func__, id); + return NULL; + } + m = 1 << (id & ISDN_P_B_MASK); + return get_Bprotocol4mask(m); +} + +int +mISDN_register_Bprotocol(struct Bprotocol *bp) +{ + u_long flags; + struct Bprotocol *old; + + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "%s: %s/%x\n", __func__, + bp->name, bp->Bprotocols); + old = get_Bprotocol4mask(bp->Bprotocols); + if (old) { + printk(KERN_WARNING + "register duplicate protocol old %s/%x new %s/%x\n", + old->name, old->Bprotocols, bp->name, bp->Bprotocols); + return -EBUSY; + } + write_lock_irqsave(&bp_lock, flags); + list_add_tail(&bp->list, &Bprotocols); + write_unlock_irqrestore(&bp_lock, flags); + return 0; +} +EXPORT_SYMBOL(mISDN_register_Bprotocol); + +void +mISDN_unregister_Bprotocol(struct Bprotocol *bp) +{ + u_long flags; + + if (debug & DEBUG_CORE) + printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name, + bp->Bprotocols); + write_lock_irqsave(&bp_lock, flags); + list_del(&bp->list); + write_unlock_irqrestore(&bp_lock, flags); +} +EXPORT_SYMBOL(mISDN_unregister_Bprotocol); + +int +mISDNInit(void) +{ + int err; + + printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n", + MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); + mISDN_initstack(&debug); + err = mISDN_inittimer(&debug); + if (err) + goto error; + err = l1_init(&debug); + if (err) { + mISDN_timer_cleanup(); + goto error; + } + err = Isdnl2_Init(&debug); + if (err) { + mISDN_timer_cleanup(); + l1_cleanup(); + goto error; + } + err = misdn_sock_init(&debug); + if (err) { + mISDN_timer_cleanup(); + l1_cleanup(); + Isdnl2_cleanup(); + } +error: + return err; +} + +void mISDN_cleanup(void) +{ + misdn_sock_cleanup(); + mISDN_timer_cleanup(); + l1_cleanup(); + Isdnl2_cleanup(); + + if (!list_empty(&devices)) + printk(KERN_ERR "%s devices still registered\n", __func__); + + if (!list_empty(&Bprotocols)) + printk(KERN_ERR "%s Bprotocols still registered\n", __func__); + printk(KERN_DEBUG "mISDNcore unloaded\n"); +} + +module_init(mISDNInit); +module_exit(mISDN_cleanup); + diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h new file mode 100644 index 000000000000..7da7233b4c1a --- /dev/null +++ b/drivers/isdn/mISDN/core.h @@ -0,0 +1,77 @@ +/* + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef mISDN_CORE_H +#define mISDN_CORE_H + +extern struct mISDNdevice *get_mdevice(u_int); +extern int get_mdevice_count(void); + +/* stack status flag */ +#define mISDN_STACK_ACTION_MASK 0x0000ffff +#define mISDN_STACK_COMMAND_MASK 0x000f0000 +#define mISDN_STACK_STATUS_MASK 0xfff00000 +/* action bits 0-15 */ +#define mISDN_STACK_WORK 0 +#define mISDN_STACK_SETUP 1 +#define mISDN_STACK_CLEARING 2 +#define mISDN_STACK_RESTART 3 +#define mISDN_STACK_WAKEUP 4 +#define mISDN_STACK_ABORT 15 +/* command bits 16-19 */ +#define mISDN_STACK_STOPPED 16 +#define mISDN_STACK_INIT 17 +#define mISDN_STACK_THREADSTART 18 +/* status bits 20-31 */ +#define mISDN_STACK_BCHANNEL 20 +#define mISDN_STACK_ACTIVE 29 +#define mISDN_STACK_RUNNING 30 +#define mISDN_STACK_KILLED 31 + + +/* manager options */ +#define MGR_OPT_USER 24 +#define MGR_OPT_NETWORK 25 + +extern int connect_Bstack(struct mISDNdevice *, struct mISDNchannel *, + u_int, struct sockaddr_mISDN *); +extern int connect_layer1(struct mISDNdevice *, struct mISDNchannel *, + u_int, struct sockaddr_mISDN *); +extern int create_l2entity(struct mISDNdevice *, struct mISDNchannel *, + u_int, struct sockaddr_mISDN *); + +extern int create_stack(struct mISDNdevice *); +extern int create_teimanager(struct mISDNdevice *); +extern void delete_teimanager(struct mISDNchannel *); +extern void delete_channel(struct mISDNchannel *); +extern void delete_stack(struct mISDNdevice *); +extern void mISDN_initstack(u_int *); +extern int misdn_sock_init(u_int *); +extern void misdn_sock_cleanup(void); +extern void add_layer2(struct mISDNchannel *, struct mISDNstack *); +extern void __add_layer2(struct mISDNchannel *, struct mISDNstack *); + +extern u_int get_all_Bprotocols(void); +struct Bprotocol *get_Bprotocol4mask(u_int); +struct Bprotocol *get_Bprotocol4id(u_int); + +extern int mISDN_inittimer(u_int *); +extern void mISDN_timer_cleanup(void); + +extern int l1_init(u_int *); +extern void l1_cleanup(void); +extern int Isdnl2_Init(u_int *); +extern void Isdnl2_cleanup(void); + +#endif diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c new file mode 100644 index 000000000000..b5d6553f2dc8 --- /dev/null +++ b/drivers/isdn/mISDN/fsm.c @@ -0,0 +1,183 @@ +/* + * finite state machine implementation + * + * Author Karsten Keil + * + * Thanks to Jan den Ouden + * Fritz Elfert + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include "fsm.h" + +#define FSM_TIMER_DEBUG 0 + +void +mISDN_FsmNew(struct Fsm *fsm, + struct FsmNode *fnlist, int fncount) +{ + int i; + + fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count * + fsm->event_count, GFP_KERNEL); + + for (i = 0; i < fncount; i++) + if ((fnlist[i].state >= fsm->state_count) || + (fnlist[i].event >= fsm->event_count)) { + printk(KERN_ERR + "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n", + i, (long)fnlist[i].state, (long)fsm->state_count, + (long)fnlist[i].event, (long)fsm->event_count); + } else + fsm->jumpmatrix[fsm->state_count * fnlist[i].event + + fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; +} +EXPORT_SYMBOL(mISDN_FsmNew); + +void +mISDN_FsmFree(struct Fsm *fsm) +{ + kfree((void *) fsm->jumpmatrix); +} +EXPORT_SYMBOL(mISDN_FsmFree); + +int +mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg) +{ + FSMFNPTR r; + + if ((fi->state >= fi->fsm->state_count) || + (event >= fi->fsm->event_count)) { + printk(KERN_ERR + "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n", + (long)fi->state, (long)fi->fsm->state_count, event, + (long)fi->fsm->event_count); + return 1; + } + r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state]; + if (r) { + if (fi->debug) + fi->printdebug(fi, "State %s Event %s", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + r(fi, event, arg); + return 0; + } else { + if (fi->debug) + fi->printdebug(fi, "State %s Event %s no action", + fi->fsm->strState[fi->state], + fi->fsm->strEvent[event]); + return 1; + } +} +EXPORT_SYMBOL(mISDN_FsmEvent); + +void +mISDN_FsmChangeState(struct FsmInst *fi, int newstate) +{ + fi->state = newstate; + if (fi->debug) + fi->printdebug(fi, "ChangeState %s", + fi->fsm->strState[newstate]); +} +EXPORT_SYMBOL(mISDN_FsmChangeState); + +static void +FsmExpireTimer(struct FsmTimer *ft) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft); +#endif + mISDN_FsmEvent(ft->fi, ft->event, ft->arg); +} + +void +mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) +{ + ft->fi = fi; + ft->tl.function = (void *) FsmExpireTimer; + ft->tl.data = (long) ft; +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft); +#endif + init_timer(&ft->tl); +} +EXPORT_SYMBOL(mISDN_FsmInitTimer); + +void +mISDN_FsmDelTimer(struct FsmTimer *ft, int where) +{ +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d", + (long) ft, where); +#endif + del_timer(&ft->tl); +} +EXPORT_SYMBOL(mISDN_FsmDelTimer); + +int +mISDN_FsmAddTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) { + if (ft->fi->debug) { + printk(KERN_WARNING + "mISDN_FsmAddTimer: timer already active!\n"); + ft->fi->printdebug(ft->fi, + "mISDN_FsmAddTimer already active!"); + } + return -1; + } + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); + return 0; +} +EXPORT_SYMBOL(mISDN_FsmAddTimer); + +void +mISDN_FsmRestartTimer(struct FsmTimer *ft, + int millisec, int event, void *arg, int where) +{ + +#if FSM_TIMER_DEBUG + if (ft->fi->debug) + ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d", + (long) ft, millisec, where); +#endif + + if (timer_pending(&ft->tl)) + del_timer(&ft->tl); + init_timer(&ft->tl); + ft->event = event; + ft->arg = arg; + ft->tl.expires = jiffies + (millisec * HZ) / 1000; + add_timer(&ft->tl); +} +EXPORT_SYMBOL(mISDN_FsmRestartTimer); diff --git a/drivers/isdn/mISDN/fsm.h b/drivers/isdn/mISDN/fsm.h new file mode 100644 index 000000000000..928f5be192c1 --- /dev/null +++ b/drivers/isdn/mISDN/fsm.h @@ -0,0 +1,67 @@ +/* + * + * Author Karsten Keil + * + * Thanks to Jan den Ouden + * Fritz Elfert + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MISDN_FSM_H +#define _MISDN_FSM_H + +#include + +/* Statemachine */ + +struct FsmInst; + +typedef void (*FSMFNPTR)(struct FsmInst *, int, void *); + +struct Fsm { + FSMFNPTR *jumpmatrix; + int state_count, event_count; + char **strEvent, **strState; +}; + +struct FsmInst { + struct Fsm *fsm; + int state; + int debug; + void *userdata; + int userint; + void (*printdebug) (struct FsmInst *, char *, ...); +}; + +struct FsmNode { + int state, event; + void (*routine) (struct FsmInst *, int, void *); +}; + +struct FsmTimer { + struct FsmInst *fi; + struct timer_list tl; + int event; + void *arg; +}; + +extern void mISDN_FsmNew(struct Fsm *, struct FsmNode *, int); +extern void mISDN_FsmFree(struct Fsm *); +extern int mISDN_FsmEvent(struct FsmInst *, int , void *); +extern void mISDN_FsmChangeState(struct FsmInst *, int); +extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *); +extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int); +extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int); +extern void mISDN_FsmDelTimer(struct FsmTimer *, int); + +#endif diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c new file mode 100644 index 000000000000..2596fba4e614 --- /dev/null +++ b/drivers/isdn/mISDN/hwchannel.c @@ -0,0 +1,365 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +static void +dchannel_bh(struct work_struct *ws) +{ + struct dchannel *dch = container_of(ws, struct dchannel, workq); + struct sk_buff *skb; + int err; + + if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) { + while ((skb = skb_dequeue(&dch->rqueue))) { + if (likely(dch->dev.D.peer)) { + err = dch->dev.D.recv(dch->dev.D.peer, skb); + if (err) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); + } + } + if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) { + if (dch->phfunc) + dch->phfunc(dch); + } +} + +static void +bchannel_bh(struct work_struct *ws) +{ + struct bchannel *bch = container_of(ws, struct bchannel, workq); + struct sk_buff *skb; + int err; + + if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) { + while ((skb = skb_dequeue(&bch->rqueue))) { + if (bch->rcount >= 64) + printk(KERN_WARNING "B-channel %p receive " + "queue if full, but empties...\n", bch); + bch->rcount--; + if (likely(bch->ch.peer)) { + err = bch->ch.recv(bch->ch.peer, skb); + if (err) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); + } + } +} + +int +mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf) +{ + test_and_set_bit(FLG_HDLC, &ch->Flags); + ch->maxlen = maxlen; + ch->hw = NULL; + ch->rx_skb = NULL; + ch->tx_skb = NULL; + ch->tx_idx = 0; + ch->phfunc = phf; + skb_queue_head_init(&ch->squeue); + skb_queue_head_init(&ch->rqueue); + INIT_LIST_HEAD(&ch->dev.bchannels); + INIT_WORK(&ch->workq, dchannel_bh); + return 0; +} +EXPORT_SYMBOL(mISDN_initdchannel); + +int +mISDN_initbchannel(struct bchannel *ch, int maxlen) +{ + ch->Flags = 0; + ch->maxlen = maxlen; + ch->hw = NULL; + ch->rx_skb = NULL; + ch->tx_skb = NULL; + ch->tx_idx = 0; + skb_queue_head_init(&ch->rqueue); + ch->rcount = 0; + ch->next_skb = NULL; + INIT_WORK(&ch->workq, bchannel_bh); + return 0; +} +EXPORT_SYMBOL(mISDN_initbchannel); + +int +mISDN_freedchannel(struct dchannel *ch) +{ + if (ch->tx_skb) { + dev_kfree_skb(ch->tx_skb); + ch->tx_skb = NULL; + } + if (ch->rx_skb) { + dev_kfree_skb(ch->rx_skb); + ch->rx_skb = NULL; + } + skb_queue_purge(&ch->squeue); + skb_queue_purge(&ch->rqueue); + flush_scheduled_work(); + return 0; +} +EXPORT_SYMBOL(mISDN_freedchannel); + +int +mISDN_freebchannel(struct bchannel *ch) +{ + if (ch->tx_skb) { + dev_kfree_skb(ch->tx_skb); + ch->tx_skb = NULL; + } + if (ch->rx_skb) { + dev_kfree_skb(ch->rx_skb); + ch->rx_skb = NULL; + } + if (ch->next_skb) { + dev_kfree_skb(ch->next_skb); + ch->next_skb = NULL; + } + skb_queue_purge(&ch->rqueue); + ch->rcount = 0; + flush_scheduled_work(); + return 0; +} +EXPORT_SYMBOL(mISDN_freebchannel); + +static inline u_int +get_sapi_tei(u_char *p) +{ + u_int sapi, tei; + + sapi = *p >> 2; + tei = p[1] >> 1; + return sapi | (tei << 8); +} + +void +recv_Dchannel(struct dchannel *dch) +{ + struct mISDNhead *hh; + + if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */ + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + return; + } + hh = mISDN_HEAD_P(dch->rx_skb); + hh->prim = PH_DATA_IND; + hh->id = get_sapi_tei(dch->rx_skb->data); + skb_queue_tail(&dch->rqueue, dch->rx_skb); + dch->rx_skb = NULL; + schedule_event(dch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Dchannel); + +void +recv_Bchannel(struct bchannel *bch) +{ + struct mISDNhead *hh; + + hh = mISDN_HEAD_P(bch->rx_skb); + hh->prim = PH_DATA_IND; + hh->id = MISDN_ID_ANY; + if (bch->rcount >= 64) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + return; + } + bch->rcount++; + skb_queue_tail(&bch->rqueue, bch->rx_skb); + bch->rx_skb = NULL; + schedule_event(bch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Bchannel); + +void +recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb) +{ + skb_queue_tail(&dch->rqueue, skb); + schedule_event(dch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Dchannel_skb); + +void +recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb) +{ + if (bch->rcount >= 64) { + dev_kfree_skb(skb); + return; + } + bch->rcount++; + skb_queue_tail(&bch->rqueue, skb); + schedule_event(bch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Bchannel_skb); + +static void +confirm_Dsend(struct dchannel *dch) +{ + struct sk_buff *skb; + + skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb), + 0, NULL, GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "%s: no skb id %x\n", __func__, + mISDN_HEAD_ID(dch->tx_skb)); + return; + } + skb_queue_tail(&dch->rqueue, skb); + schedule_event(dch, FLG_RECVQUEUE); +} + +int +get_next_dframe(struct dchannel *dch) +{ + dch->tx_idx = 0; + dch->tx_skb = skb_dequeue(&dch->squeue); + if (dch->tx_skb) { + confirm_Dsend(dch); + return 1; + } + dch->tx_skb = NULL; + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + return 0; +} +EXPORT_SYMBOL(get_next_dframe); + +void +confirm_Bsend(struct bchannel *bch) +{ + struct sk_buff *skb; + + if (bch->rcount >= 64) + return; + skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb), + 0, NULL, GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "%s: no skb id %x\n", __func__, + mISDN_HEAD_ID(bch->tx_skb)); + return; + } + bch->rcount++; + skb_queue_tail(&bch->rqueue, skb); + schedule_event(bch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(confirm_Bsend); + +int +get_next_bframe(struct bchannel *bch) +{ + bch->tx_idx = 0; + if (test_bit(FLG_TX_NEXT, &bch->Flags)) { + bch->tx_skb = bch->next_skb; + if (bch->tx_skb) { + bch->next_skb = NULL; + test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + confirm_Bsend(bch); /* not for transparent */ + return 1; + } else { + test_and_clear_bit(FLG_TX_NEXT, &bch->Flags); + printk(KERN_WARNING "B TX_NEXT without skb\n"); + } + } + bch->tx_skb = NULL; + test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); + return 0; +} +EXPORT_SYMBOL(get_next_bframe); + +void +queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb) +{ + struct mISDNhead *hh; + + if (!skb) { + _queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC); + } else { + if (ch->peer) { + hh = mISDN_HEAD_P(skb); + hh->prim = pr; + hh->id = id; + if (!ch->recv(ch->peer, skb)) + return; + } + dev_kfree_skb(skb); + } +} +EXPORT_SYMBOL(queue_ch_frame); + +int +dchannel_senddata(struct dchannel *ch, struct sk_buff *skb) +{ + /* check oversize */ + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", __func__); + return -EINVAL; + } + if (skb->len > ch->maxlen) { + printk(KERN_WARNING "%s: skb too large(%d/%d)\n", + __func__, skb->len, ch->maxlen); + return -EINVAL; + } + /* HW lock must be obtained */ + if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { + skb_queue_tail(&ch->squeue, skb); + return 0; + } else { + /* write to fifo */ + ch->tx_skb = skb; + ch->tx_idx = 0; + return 1; + } +} +EXPORT_SYMBOL(dchannel_senddata); + +int +bchannel_senddata(struct bchannel *ch, struct sk_buff *skb) +{ + + /* check oversize */ + if (skb->len <= 0) { + printk(KERN_WARNING "%s: skb too small\n", __func__); + return -EINVAL; + } + if (skb->len > ch->maxlen) { + printk(KERN_WARNING "%s: skb too large(%d/%d)\n", + __func__, skb->len, ch->maxlen); + return -EINVAL; + } + /* HW lock must be obtained */ + /* check for pending next_skb */ + if (ch->next_skb) { + printk(KERN_WARNING + "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", + __func__, skb->len, ch->next_skb->len); + return -EBUSY; + } + if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) { + test_and_set_bit(FLG_TX_NEXT, &ch->Flags); + ch->next_skb = skb; + return 0; + } else { + /* write to fifo */ + ch->tx_skb = skb; + ch->tx_idx = 0; + return 1; + } +} +EXPORT_SYMBOL(bchannel_senddata); diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c new file mode 100644 index 000000000000..fced1a2755f8 --- /dev/null +++ b/drivers/isdn/mISDN/layer1.c @@ -0,0 +1,403 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#include +#include +#include "layer1.h" +#include "fsm.h" + +static int *debug; + +struct layer1 { + u_long Flags; + struct FsmInst l1m; + struct FsmTimer timer; + int delay; + struct dchannel *dch; + dchannel_l1callback *dcb; +}; + +#define TIMER3_VALUE 7000 + +static +struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_F2, + ST_L1_F3, + ST_L1_F4, + ST_L1_F5, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1S_STATE_COUNT (ST_L1_F8+1) + +static char *strL1SState[] = +{ + "ST_L1_F2", + "ST_L1_F3", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +enum { + EV_PH_ACTIVATE, + EV_PH_DEACTIVATE, + EV_RESET_IND, + EV_DEACT_CNF, + EV_DEACT_IND, + EV_POWER_UP, + EV_ANYSIG_IND, + EV_INFO2_IND, + EV_INFO4_IND, + EV_TIMER_DEACT, + EV_TIMER_ACT, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +static char *strL1Event[] = +{ + "EV_PH_ACTIVATE", + "EV_PH_DEACTIVATE", + "EV_RESET_IND", + "EV_DEACT_CNF", + "EV_DEACT_IND", + "EV_POWER_UP", + "EV_ANYSIG_IND", + "EV_INFO2_IND", + "EV_INFO4_IND", + "EV_TIMER_DEACT", + "EV_TIMER_ACT", + "EV_TIMER3", +}; + +static void +l1m_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct layer1 *l1 = fi->userdata; + va_list va; + + va_start(va, fmt); + printk(KERN_DEBUG "%s: ", l1->dch->dev.name); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + +static void +l1_reset(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_deact_cnf(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F3); + if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) + l1->dcb(l1->dch, HW_POWERUP_REQ); +} + +static void +l1_deact_req_s(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F3); + mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2); + test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags); +} + +static void +l1_power_up_s(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) { + mISDN_FsmChangeState(fi, ST_L1_F4); + l1->dcb(l1->dch, INFO3_P8); + } else + mISDN_FsmChangeState(fi, ST_L1_F3); +} + +static void +l1_go_F5(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F5); +} + +static void +l1_go_F8(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_F8); +} + +static void +l1_info2_ind(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F6); + l1->dcb(l1->dch, INFO3_P8); +} + +static void +l1_info4_ind(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L1_F7); + l1->dcb(l1->dch, INFO3_P8); + if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags)) + mISDN_FsmDelTimer(&l1->timer, 4); + if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) { + if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags)) + mISDN_FsmDelTimer(&l1->timer, 3); + mISDN_FsmRestartTimer(&l1->timer, 110, EV_TIMER_ACT, NULL, 2); + test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags); + } +} + +static void +l1_timer3(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags); + if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) { + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1->dcb(l1->dch, HW_D_NOBLOCKED); + l1->dcb(l1->dch, PH_DEACTIVATE_IND); + } + if (l1->l1m.state != ST_L1_F6) { + mISDN_FsmChangeState(fi, ST_L1_F3); + l1->dcb(l1->dch, HW_POWERUP_REQ); + } +} + +static void +l1_timer_act(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags); + test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags); + l1->dcb(l1->dch, PH_ACTIVATE_IND); +} + +static void +l1_timer_deact(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags); + test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags); + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1->dcb(l1->dch, HW_D_NOBLOCKED); + l1->dcb(l1->dch, PH_DEACTIVATE_IND); + l1->dcb(l1->dch, HW_DEACT_REQ); +} + +static void +l1_activate_s(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + test_and_set_bit(FLG_L1_T3RUN, &l1->Flags); + l1->dcb(l1->dch, HW_RESET_REQ); +} + +static void +l1_activate_no(struct FsmInst *fi, int event, void *arg) +{ + struct layer1 *l1 = fi->userdata; + + if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) && + (!test_bit(FLG_L1_T3RUN, &l1->Flags))) { + test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags); + if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) + l1->dcb(l1->dch, HW_D_NOBLOCKED); + l1->dcb(l1->dch, PH_DEACTIVATE_IND); + } +} + +static struct FsmNode L1SFnList[] = +{ + {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s}, + {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, + {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, + {ST_L1_F3, EV_RESET_IND, l1_reset}, + {ST_L1_F4, EV_RESET_IND, l1_reset}, + {ST_L1_F5, EV_RESET_IND, l1_reset}, + {ST_L1_F6, EV_RESET_IND, l1_reset}, + {ST_L1_F7, EV_RESET_IND, l1_reset}, + {ST_L1_F8, EV_RESET_IND, l1_reset}, + {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, + {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s}, + {ST_L1_F3, EV_POWER_UP, l1_power_up_s}, + {ST_L1_F4, EV_ANYSIG_IND, l1_go_F5}, + {ST_L1_F6, EV_ANYSIG_IND, l1_go_F8}, + {ST_L1_F7, EV_ANYSIG_IND, l1_go_F8}, + {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, + {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, + {ST_L1_F3, EV_TIMER3, l1_timer3}, + {ST_L1_F4, EV_TIMER3, l1_timer3}, + {ST_L1_F5, EV_TIMER3, l1_timer3}, + {ST_L1_F6, EV_TIMER3, l1_timer3}, + {ST_L1_F8, EV_TIMER3, l1_timer3}, + {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, + {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, + {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, +}; + +static void +release_l1(struct layer1 *l1) { + mISDN_FsmDelTimer(&l1->timer, 0); + if (l1->dch) + l1->dch->l1 = NULL; + module_put(THIS_MODULE); + kfree(l1); +} + +int +l1_event(struct layer1 *l1, u_int event) +{ + int err = 0; + + if (!l1) + return -EINVAL; + switch (event) { + case HW_RESET_IND: + mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL); + break; + case HW_DEACT_IND: + mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL); + break; + case HW_POWERUP_IND: + mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL); + break; + case HW_DEACT_CNF: + mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL); + break; + case ANYSIGNAL: + mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); + break; + case LOSTFRAMING: + mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); + break; + case INFO2: + mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL); + break; + case INFO4_P8: + mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); + break; + case INFO4_P10: + mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); + break; + case PH_ACTIVATE_REQ: + if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) + l1->dcb(l1->dch, PH_ACTIVATE_IND); + else { + test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags); + mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL); + } + break; + case CLOSE_CHANNEL: + release_l1(l1); + break; + default: + if (*debug & DEBUG_L1) + printk(KERN_DEBUG "%s %x unhandled\n", + __func__, event); + err = -EINVAL; + } + return err; +} +EXPORT_SYMBOL(l1_event); + +int +create_l1(struct dchannel *dch, dchannel_l1callback *dcb) { + struct layer1 *nl1; + + nl1 = kzalloc(sizeof(struct layer1), GFP_ATOMIC); + if (!nl1) { + printk(KERN_ERR "kmalloc struct layer1 failed\n"); + return -ENOMEM; + } + nl1->l1m.fsm = &l1fsm_s; + nl1->l1m.state = ST_L1_F3; + nl1->Flags = 0; + nl1->l1m.debug = *debug & DEBUG_L1_FSM; + nl1->l1m.userdata = nl1; + nl1->l1m.userint = 0; + nl1->l1m.printdebug = l1m_debug; + nl1->dch = dch; + nl1->dcb = dcb; + mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer); + __module_get(THIS_MODULE); + dch->l1 = nl1; + return 0; +} +EXPORT_SYMBOL(create_l1); + +int +l1_init(u_int *deb) +{ + debug = deb; + l1fsm_s.state_count = L1S_STATE_COUNT; + l1fsm_s.event_count = L1_EVENT_COUNT; + l1fsm_s.strEvent = strL1Event; + l1fsm_s.strState = strL1SState; + mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList)); + return 0; +} + +void +l1_cleanup(void) +{ + mISDN_FsmFree(&l1fsm_s); +} diff --git a/drivers/isdn/mISDN/layer1.h b/drivers/isdn/mISDN/layer1.h new file mode 100644 index 000000000000..9c8125fd89af --- /dev/null +++ b/drivers/isdn/mISDN/layer1.h @@ -0,0 +1,26 @@ +/* + * + * Layer 1 defines + * + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define FLG_L1_ACTIVATING 1 +#define FLG_L1_ACTIVATED 2 +#define FLG_L1_DEACTTIMER 3 +#define FLG_L1_ACTTIMER 4 +#define FLG_L1_T3RUN 5 +#define FLG_L1_PULL_REQ 6 +#define FLG_L1_UINT 7 +#define FLG_L1_DBLOCKED 8 + diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c new file mode 100644 index 000000000000..f5ad888ee71e --- /dev/null +++ b/drivers/isdn/mISDN/layer2.c @@ -0,0 +1,2216 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "fsm.h" +#include "layer2.h" + +static int *debug; + +static +struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL}; + +static char *strL2State[] = +{ + "ST_L2_1", + "ST_L2_2", + "ST_L2_3", + "ST_L2_4", + "ST_L2_5", + "ST_L2_6", + "ST_L2_7", + "ST_L2_8", +}; + +enum { + EV_L2_UI, + EV_L2_SABME, + EV_L2_DISC, + EV_L2_DM, + EV_L2_UA, + EV_L2_FRMR, + EV_L2_SUPER, + EV_L2_I, + EV_L2_DL_DATA, + EV_L2_ACK_PULL, + EV_L2_DL_UNITDATA, + EV_L2_DL_ESTABLISH_REQ, + EV_L2_DL_RELEASE_REQ, + EV_L2_MDL_ASSIGN, + EV_L2_MDL_REMOVE, + EV_L2_MDL_ERROR, + EV_L1_DEACTIVATE, + EV_L2_T200, + EV_L2_T203, + EV_L2_SET_OWN_BUSY, + EV_L2_CLEAR_OWN_BUSY, + EV_L2_FRAME_ERROR, +}; + +#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1) + +static char *strL2Event[] = +{ + "EV_L2_UI", + "EV_L2_SABME", + "EV_L2_DISC", + "EV_L2_DM", + "EV_L2_UA", + "EV_L2_FRMR", + "EV_L2_SUPER", + "EV_L2_I", + "EV_L2_DL_DATA", + "EV_L2_ACK_PULL", + "EV_L2_DL_UNITDATA", + "EV_L2_DL_ESTABLISH_REQ", + "EV_L2_DL_RELEASE_REQ", + "EV_L2_MDL_ASSIGN", + "EV_L2_MDL_REMOVE", + "EV_L2_MDL_ERROR", + "EV_L1_DEACTIVATE", + "EV_L2_T200", + "EV_L2_T203", + "EV_L2_SET_OWN_BUSY", + "EV_L2_CLEAR_OWN_BUSY", + "EV_L2_FRAME_ERROR", +}; + +static void +l2m_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct layer2 *l2 = fi->userdata; + va_list va; + + if (!(*debug & DEBUG_L2_FSM)) + return; + va_start(va, fmt); + printk(KERN_DEBUG "l2 (tei %d): ", l2->tei); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + +inline u_int +l2headersize(struct layer2 *l2, int ui) +{ + return ((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) + + (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1); +} + +inline u_int +l2addrsize(struct layer2 *l2) +{ + return test_bit(FLG_LAPD, &l2->flag) ? 2 : 1; +} + +static u_int +l2_newid(struct layer2 *l2) +{ + u_int id; + + id = l2->next_id++; + if (id == 0x7fff) + l2->next_id = 1; + id <<= 16; + id |= l2->tei << 8; + id |= l2->sapi; + return id; +} + +static void +l2up(struct layer2 *l2, u_int prim, struct sk_buff *skb) +{ + int err; + + if (!l2->up) + return; + mISDN_HEAD_PRIM(skb) = prim; + mISDN_HEAD_ID(skb) = (l2->ch.nr << 16) | l2->ch.addr; + err = l2->up->send(l2->up, skb); + if (err) { + printk(KERN_WARNING "%s: err=%d\n", __func__, err); + dev_kfree_skb(skb); + } +} + +static void +l2up_create(struct layer2 *l2, u_int prim, int len, void *arg) +{ + struct sk_buff *skb; + struct mISDNhead *hh; + int err; + + if (!l2->up) + return; + skb = mI_alloc_skb(len, GFP_ATOMIC); + if (!skb) + return; + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = (l2->ch.nr << 16) | l2->ch.addr; + if (len) + memcpy(skb_put(skb, len), arg, len); + err = l2->up->send(l2->up, skb); + if (err) { + printk(KERN_WARNING "%s: err=%d\n", __func__, err); + dev_kfree_skb(skb); + } +} + +static int +l2down_skb(struct layer2 *l2, struct sk_buff *skb) { + int ret; + + ret = l2->ch.recv(l2->ch.peer, skb); + if (ret && (*debug & DEBUG_L2_RECV)) + printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret); + return ret; +} + +static int +l2down_raw(struct layer2 *l2, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + + if (hh->prim == PH_DATA_REQ) { + if (test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) { + skb_queue_tail(&l2->down_queue, skb); + return 0; + } + l2->down_id = mISDN_HEAD_ID(skb); + } + return l2down_skb(l2, skb); +} + +static int +l2down(struct layer2 *l2, u_int prim, u_int id, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + + hh->prim = prim; + hh->id = id; + return l2down_raw(l2, skb); +} + +static int +l2down_create(struct layer2 *l2, u_int prim, u_int id, int len, void *arg) +{ + struct sk_buff *skb; + int err; + struct mISDNhead *hh; + + skb = mI_alloc_skb(len, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = id; + if (len) + memcpy(skb_put(skb, len), arg, len); + err = l2down_raw(l2, skb); + if (err) + dev_kfree_skb(skb); + return err; +} + +static int +ph_data_confirm(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) { + struct sk_buff *nskb = skb; + int ret = -EAGAIN; + + if (test_bit(FLG_L1_NOTREADY, &l2->flag)) { + if (hh->id == l2->down_id) { + nskb = skb_dequeue(&l2->down_queue); + if (nskb) { + l2->down_id = mISDN_HEAD_ID(nskb); + if (l2down_skb(l2, nskb)) { + dev_kfree_skb(nskb); + l2->down_id = MISDN_ID_NONE; + } + } else + l2->down_id = MISDN_ID_NONE; + if (ret) { + dev_kfree_skb(skb); + ret = 0; + } + if (l2->down_id == MISDN_ID_NONE) { + test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); + mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); + } + } + } + if (!test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) { + nskb = skb_dequeue(&l2->down_queue); + if (nskb) { + l2->down_id = mISDN_HEAD_ID(nskb); + if (l2down_skb(l2, nskb)) { + dev_kfree_skb(nskb); + l2->down_id = MISDN_ID_NONE; + test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); + } + } else + test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag); + } + return ret; +} + +static int +l2mgr(struct layer2 *l2, u_int prim, void *arg) { + long c = (long)arg; + + printk(KERN_WARNING + "l2mgr: addr:%x prim %x %c\n", l2->id, prim, (char)c); + if (test_bit(FLG_LAPD, &l2->flag) && + !test_bit(FLG_FIXED_TEI, &l2->flag)) { + switch (c) { + case 'C': + case 'D': + case 'G': + case 'H': + l2_tei(l2, prim, (u_long)arg); + break; + } + } + return 0; +} + +static void +set_peer_busy(struct layer2 *l2) { + test_and_set_bit(FLG_PEER_BUSY, &l2->flag); + if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue)) + test_and_set_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +clear_peer_busy(struct layer2 *l2) { + if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag)) + test_and_clear_bit(FLG_L2BLOCK, &l2->flag); +} + +static void +InitWin(struct layer2 *l2) +{ + int i; + + for (i = 0; i < MAX_WINDOW; i++) + l2->windowar[i] = NULL; +} + +static int +freewin(struct layer2 *l2) +{ + int i, cnt = 0; + + for (i = 0; i < MAX_WINDOW; i++) { + if (l2->windowar[i]) { + cnt++; + dev_kfree_skb(l2->windowar[i]); + l2->windowar[i] = NULL; + } + } + return cnt; +} + +static void +ReleaseWin(struct layer2 *l2) +{ + int cnt = freewin(l2); + + if (cnt) + printk(KERN_WARNING + "isdnl2 freed %d skbuffs in release\n", cnt); +} + +inline unsigned int +cansend(struct layer2 *l2) +{ + unsigned int p1; + + if (test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; + return (p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag); +} + +inline void +clear_exception(struct layer2 *l2) +{ + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + test_and_clear_bit(FLG_REJEXC, &l2->flag); + test_and_clear_bit(FLG_OWN_BUSY, &l2->flag); + clear_peer_busy(l2); +} + +static int +sethdraddr(struct layer2 *l2, u_char *header, int rsp) +{ + u_char *ptr = header; + int crbit = rsp; + + if (test_bit(FLG_LAPD, &l2->flag)) { + if (test_bit(FLG_LAPD_NET, &l2->flag)) + crbit = !crbit; + *ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0); + *ptr++ = (l2->tei << 1) | 1; + return 2; + } else { + if (test_bit(FLG_ORIG, &l2->flag)) + crbit = !crbit; + if (crbit) + *ptr++ = l2->addr.B; + else + *ptr++ = l2->addr.A; + return 1; + } +} + +static inline void +enqueue_super(struct layer2 *l2, struct sk_buff *skb) +{ + if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb)) + dev_kfree_skb(skb); +} + +static inline void +enqueue_ui(struct layer2 *l2, struct sk_buff *skb) +{ + if (l2->tm) + l2_tei(l2, MDL_STATUS_UI_IND, 0); + if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb)) + dev_kfree_skb(skb); +} + +inline int +IsUI(u_char *data) +{ + return (data[0] & 0xef) == UI; +} + +inline int +IsUA(u_char *data) +{ + return (data[0] & 0xef) == UA; +} + +inline int +IsDM(u_char *data) +{ + return (data[0] & 0xef) == DM; +} + +inline int +IsDISC(u_char *data) +{ + return (data[0] & 0xef) == DISC; +} + +inline int +IsRR(u_char *data, struct layer2 *l2) +{ + if (test_bit(FLG_MOD128, &l2->flag)) + return data[0] == RR; + else + return (data[0] & 0xf) == 1; +} + +inline int +IsSFrame(u_char *data, struct layer2 *l2) +{ + register u_char d = *data; + + if (!test_bit(FLG_MOD128, &l2->flag)) + d &= 0xf; + return ((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c); +} + +inline int +IsSABME(u_char *data, struct layer2 *l2) +{ + u_char d = data[0] & ~0x10; + + return test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM; +} + +inline int +IsREJ(u_char *data, struct layer2 *l2) +{ + return test_bit(FLG_MOD128, &l2->flag) ? + data[0] == REJ : (data[0] & 0xf) == REJ; +} + +inline int +IsFRMR(u_char *data) +{ + return (data[0] & 0xef) == FRMR; +} + +inline int +IsRNR(u_char *data, struct layer2 *l2) +{ + return test_bit(FLG_MOD128, &l2->flag) ? + data[0] == RNR : (data[0] & 0xf) == RNR; +} + +int +iframe_error(struct layer2 *l2, struct sk_buff *skb) +{ + u_int i; + int rsp = *skb->data & 0x2; + + i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1); + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp) + return 'L'; + if (skb->len < i) + return 'N'; + if ((skb->len - i) > l2->maxlen) + return 'O'; + return 0; +} + +int +super_error(struct layer2 *l2, struct sk_buff *skb) +{ + if (skb->len != l2addrsize(l2) + + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1)) + return 'N'; + return 0; +} + +int +unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp) +{ + int rsp = (*skb->data & 0x2) >> 1; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp != wantrsp) + return 'L'; + if (skb->len != l2addrsize(l2) + 1) + return 'N'; + return 0; +} + +int +UI_error(struct layer2 *l2, struct sk_buff *skb) +{ + int rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (rsp) + return 'L'; + if (skb->len > l2->maxlen + l2addrsize(l2) + 1) + return 'O'; + return 0; +} + +int +FRMR_error(struct layer2 *l2, struct sk_buff *skb) +{ + u_int headers = l2addrsize(l2) + 1; + u_char *datap = skb->data + headers; + int rsp = *skb->data & 0x2; + + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + if (!rsp) + return 'L'; + if (test_bit(FLG_MOD128, &l2->flag)) { + if (skb->len < headers + 5) + return 'N'; + else if (*debug & DEBUG_L2) + l2m_debug(&l2->l2m, + "FRMR information %2x %2x %2x %2x %2x", + datap[0], datap[1], datap[2], datap[3], datap[4]); + } else { + if (skb->len < headers + 3) + return 'N'; + else if (*debug & DEBUG_L2) + l2m_debug(&l2->l2m, + "FRMR information %2x %2x %2x", + datap[0], datap[1], datap[2]); + } + return 0; +} + +static unsigned int +legalnr(struct layer2 *l2, unsigned int nr) +{ + if (test_bit(FLG_MOD128, &l2->flag)) + return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128); + else + return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8); +} + +static void +setva(struct layer2 *l2, unsigned int nr) +{ + struct sk_buff *skb; + + while (l2->va != nr) { + l2->va++; + if (test_bit(FLG_MOD128, &l2->flag)) + l2->va %= 128; + else + l2->va %= 8; + if (l2->windowar[l2->sow]) { + skb_trim(l2->windowar[l2->sow], 0); + skb_queue_tail(&l2->tmp_queue, l2->windowar[l2->sow]); + l2->windowar[l2->sow] = NULL; + } + l2->sow = (l2->sow + 1) % l2->window; + } + skb = skb_dequeue(&l2->tmp_queue); + while (skb) { + dev_kfree_skb(skb); + skb = skb_dequeue(&l2->tmp_queue); + } +} + +static void +send_uframe(struct layer2 *l2, struct sk_buff *skb, u_char cmd, u_char cr) +{ + u_char tmp[MAX_L2HEADER_LEN]; + int i; + + i = sethdraddr(l2, tmp, cr); + tmp[i++] = cmd; + if (skb) + skb_trim(skb, 0); + else { + skb = mI_alloc_skb(i, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING "%s: can't alloc skbuff\n", + __func__); + return; + } + } + memcpy(skb_put(skb, i), tmp, i); + enqueue_super(l2, skb); +} + + +inline u_char +get_PollFlag(struct layer2 *l2, struct sk_buff *skb) +{ + return skb->data[l2addrsize(l2)] & 0x10; +} + +inline u_char +get_PollFlagFree(struct layer2 *l2, struct sk_buff *skb) +{ + u_char PF; + + PF = get_PollFlag(l2, skb); + dev_kfree_skb(skb); + return PF; +} + +inline void +start_t200(struct layer2 *l2, int i) +{ + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &l2->flag); +} + +inline void +restart_t200(struct layer2 *l2, int i) +{ + mISDN_FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i); + test_and_set_bit(FLG_T200_RUN, &l2->flag); +} + +inline void +stop_t200(struct layer2 *l2, int i) +{ + if (test_and_clear_bit(FLG_T200_RUN, &l2->flag)) + mISDN_FsmDelTimer(&l2->t200, i); +} + +inline void +st5_dl_release_l2l3(struct layer2 *l2) +{ + int pr; + + if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) + pr = DL_RELEASE_CNF; + else + pr = DL_RELEASE_IND; + l2up_create(l2, pr, 0, NULL); +} + +inline void +lapb_dl_release_l2l3(struct layer2 *l2, int f) +{ + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, l2_newid(l2), 0, NULL); + l2up_create(l2, f, 0, NULL); +} + +static void +establishlink(struct FsmInst *fi) +{ + struct layer2 *l2 = fi->userdata; + u_char cmd; + + clear_exception(l2); + l2->rc = 0; + cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10; + send_uframe(l2, NULL, cmd, CMD); + mISDN_FsmDelTimer(&l2->t203, 1); + restart_t200(l2, 1); + test_and_clear_bit(FLG_PEND_REL, &l2->flag); + freewin(l2); + mISDN_FsmChangeState(fi, ST_L2_5); +} + +static void +l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR_IND, (void *) 'C'); + else + l2mgr(l2, MDL_ERROR_IND, (void *) 'D'); + +} + +static void +l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR_IND, (void *) 'B'); + else { + l2mgr(l2, MDL_ERROR_IND, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } +} + +static void +l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + if (get_PollFlagFree(l2, skb)) + l2mgr(l2, MDL_ERROR_IND, (void *) 'B'); + else + l2mgr(l2, MDL_ERROR_IND, (void *) 'E'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static void +l2_go_st3(struct FsmInst *fi, int event, void *arg) +{ + dev_kfree_skb((struct sk_buff *)arg); + mISDN_FsmChangeState(fi, ST_L2_3); +} + +static void +l2_mdl_assign(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + mISDN_FsmChangeState(fi, ST_L2_3); + dev_kfree_skb((struct sk_buff *)arg); + l2_tei(l2, MDL_ASSIGN_IND, 0); +} + +static void +l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); + mISDN_FsmChangeState(fi, ST_L2_2); + l2_tei(l2, MDL_ASSIGN_IND, 0); +} + +static void +l2_queue_ui(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); +} + +static void +tx_ui(struct layer2 *l2) +{ + struct sk_buff *skb; + u_char header[MAX_L2HEADER_LEN]; + int i; + + i = sethdraddr(l2, header, CMD); + if (test_bit(FLG_LAPD_NET, &l2->flag)) + header[1] = 0xff; /* tei 127 */ + header[i++] = UI; + while ((skb = skb_dequeue(&l2->ui_queue))) { + memcpy(skb_push(skb, i), header, i); + enqueue_ui(l2, skb); + } +} + +static void +l2_send_ui(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->ui_queue, skb); + tx_ui(l2); +} + +static void +l2_got_ui(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_pull(skb, l2headersize(l2, 1)); +/* + * in states 1-3 for broadcast + */ + + if (l2->tm) + l2_tei(l2, MDL_STATUS_UI_IND, 0); + l2up(l2, DL_UNITDATA_IND, skb); +} + +static void +l2_establish(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + test_and_clear_bit(FLG_PEND_REL, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_l3_reestablish(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_release(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_trim(skb, 0); + l2up(l2, DL_RELEASE_CNF, skb); +} + +static void +l2_pend_rel(struct FsmInst *fi, int event, void *arg) +{ + struct sk_buff *skb = arg; + struct layer2 *l2 = fi->userdata; + + test_and_set_bit(FLG_PEND_REL, &l2->flag); + dev_kfree_skb(skb); +} + +static void +l2_disconnect(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + freewin(l2); + mISDN_FsmChangeState(fi, ST_L2_6); + l2->rc = 0; + send_uframe(l2, NULL, DISC | 0x10, CMD); + mISDN_FsmDelTimer(&l2->t203, 1); + restart_t200(l2, 2); + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_start_multi(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + l2->vs = 0; + l2->va = 0; + l2->vr = 0; + l2->sow = 0; + clear_exception(l2); + send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP); + mISDN_FsmChangeState(fi, ST_L2_7); + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); + skb_trim(skb, 0); + l2up(l2, DL_ESTABLISH_IND, skb); + if (l2->tm) + l2_tei(l2, MDL_STATUS_UP_IND, 0); +} + +static void +l2_send_UA(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); +} + +static void +l2_send_DM(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + send_uframe(l2, skb, DM | get_PollFlag(l2, skb), RSP); +} + +static void +l2_restart_multi(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int est = 0; + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); + + l2mgr(l2, MDL_ERROR_IND, (void *) 'F'); + + if (l2->vs != l2->va) { + skb_queue_purge(&l2->i_queue); + est = 1; + } + + clear_exception(l2); + l2->vs = 0; + l2->va = 0; + l2->vr = 0; + l2->sow = 0; + mISDN_FsmChangeState(fi, ST_L2_7); + stop_t200(l2, 3); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); + + if (est) + l2up_create(l2, DL_ESTABLISH_IND, 0, NULL); +/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST, + * MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED, + * 0, NULL, 0); + */ + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_stop_multi(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + mISDN_FsmChangeState(fi, ST_L2_4); + mISDN_FsmDelTimer(&l2->t203, 3); + stop_t200(l2, 4); + + send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); + skb_queue_purge(&l2->i_queue); + freewin(l2); + lapb_dl_release_l2l3(l2, DL_RELEASE_IND); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_connected(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int pr = -1; + + if (!get_PollFlag(l2, skb)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + dev_kfree_skb(skb); + if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) + l2_disconnect(fi, event, NULL); + if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) { + pr = DL_ESTABLISH_CNF; + } else if (l2->vs != l2->va) { + skb_queue_purge(&l2->i_queue); + pr = DL_ESTABLISH_IND; + } + stop_t200(l2, 5); + l2->vr = 0; + l2->vs = 0; + l2->va = 0; + l2->sow = 0; + mISDN_FsmChangeState(fi, ST_L2_7); + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4); + if (pr != -1) + l2up_create(l2, pr, 0, NULL); + + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + + if (l2->tm) + l2_tei(l2, MDL_STATUS_UP_IND, 0); +} + +static void +l2_released(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlag(l2, skb)) { + l2_mdl_error_ua(fi, event, arg); + return; + } + dev_kfree_skb(skb); + stop_t200(l2, 6); + lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_reestablish(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!get_PollFlagFree(l2, skb)) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } +} + +static void +l2_st5_dm_release(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (get_PollFlagFree(l2, skb)) { + stop_t200(l2, 7); + if (!test_bit(FLG_L3_INIT, &l2->flag)) + skb_queue_purge(&l2->i_queue); + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, + l2_newid(l2), 0, NULL); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } +} + +static void +l2_st6_dm_release(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (get_PollFlagFree(l2, skb)) { + stop_t200(l2, 8); + lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } +} + +void +enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf) +{ + struct sk_buff *skb; + u_char tmp[MAX_L2HEADER_LEN]; + int i; + + i = sethdraddr(l2, tmp, cr); + if (test_bit(FLG_MOD128, &l2->flag)) { + tmp[i++] = typ; + tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0); + } else + tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0); + skb = mI_alloc_skb(i, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING + "isdnl2 can't alloc sbbuff for enquiry_cr\n"); + return; + } + memcpy(skb_put(skb, i), tmp, i); + enqueue_super(l2, skb); +} + +inline void +enquiry_response(struct layer2 *l2) +{ + if (test_bit(FLG_OWN_BUSY, &l2->flag)) + enquiry_cr(l2, RNR, RSP, 1); + else + enquiry_cr(l2, RR, RSP, 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); +} + +inline void +transmit_enquiry(struct layer2 *l2) +{ + if (test_bit(FLG_OWN_BUSY, &l2->flag)) + enquiry_cr(l2, RNR, CMD, 1); + else + enquiry_cr(l2, RR, CMD, 1); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + start_t200(l2, 9); +} + + +static void +nrerrorrecovery(struct FsmInst *fi) +{ + struct layer2 *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR_IND, (void *) 'J'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static void +invoke_retransmission(struct layer2 *l2, unsigned int nr) +{ + u_int p1; + + if (l2->vs != nr) { + while (l2->vs != nr) { + (l2->vs)--; + if (test_bit(FLG_MOD128, &l2->flag)) { + l2->vs %= 128; + p1 = (l2->vs - l2->va) % 128; + } else { + l2->vs %= 8; + p1 = (l2->vs - l2->va) % 8; + } + p1 = (p1 + l2->sow) % l2->window; + if (l2->windowar[p1]) + skb_queue_head(&l2->i_queue, l2->windowar[p1]); + else + printk(KERN_WARNING + "%s: windowar[%d] is NULL\n", + __func__, p1); + l2->windowar[p1] = NULL; + } + mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL); + } +} + +static void +l2_st7_got_super(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, rsp, typ = RR; + unsigned int nr; + + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + skb_pull(skb, l2addrsize(l2)); + if (IsRNR(skb->data, l2)) { + set_peer_busy(l2); + typ = RNR; + } else + clear_peer_busy(l2); + if (IsREJ(skb->data, l2)) + typ = REJ; + + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } + dev_kfree_skb(skb); + + if (PollFlag) { + if (rsp) + l2mgr(l2, MDL_ERROR_IND, (void *) 'A'); + else + enquiry_response(l2); + } + if (legalnr(l2, nr)) { + if (typ == REJ) { + setva(l2, nr); + invoke_retransmission(l2, nr); + stop_t200(l2, 10); + if (mISDN_FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 6)) + l2m_debug(&l2->l2m, "Restart T203 ST7 REJ"); + } else if ((nr == l2->vs) && (typ == RR)) { + setva(l2, nr); + stop_t200(l2, 11); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 7); + } else if ((l2->va != nr) || (typ == RNR)) { + setva(l2, nr); + if (typ != RR) + mISDN_FsmDelTimer(&l2->t203, 9); + restart_t200(l2, 12); + } + if (skb_queue_len(&l2->i_queue) && (typ == RR)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + } else + nrerrorrecovery(fi); +} + +static void +l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_bit(FLG_L3_INIT, &l2->flag)) + skb_queue_tail(&l2->i_queue, skb); + else + dev_kfree_skb(skb); +} + +static void +l2_feed_i_pull(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->i_queue, skb); + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); +} + +static void +l2_feed_iqueue(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_tail(&l2->i_queue, skb); +} + +static void +l2_got_iframe(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, i; + u_int ns, nr; + + i = l2addrsize(l2); + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = ((skb->data[i + 1] & 0x1) == 0x1); + ns = skb->data[i] >> 1; + nr = (skb->data[i + 1] >> 1) & 0x7f; + } else { + PollFlag = (skb->data[i] & 0x10); + ns = (skb->data[i] >> 1) & 0x7; + nr = (skb->data[i] >> 5) & 0x7; + } + if (test_bit(FLG_OWN_BUSY, &l2->flag)) { + dev_kfree_skb(skb); + if (PollFlag) + enquiry_response(l2); + } else { + if (l2->vr == ns) { + l2->vr++; + if (test_bit(FLG_MOD128, &l2->flag)) + l2->vr %= 128; + else + l2->vr %= 8; + test_and_clear_bit(FLG_REJEXC, &l2->flag); + if (PollFlag) + enquiry_response(l2); + else + test_and_set_bit(FLG_ACK_PEND, &l2->flag); + skb_pull(skb, l2headersize(l2, 0)); + l2up(l2, DL_DATA_IND, skb); + } else { + /* n(s)!=v(r) */ + dev_kfree_skb(skb); + if (test_and_set_bit(FLG_REJEXC, &l2->flag)) { + if (PollFlag) + enquiry_response(l2); + } else { + enquiry_cr(l2, REJ, RSP, PollFlag); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + } + } + if (legalnr(l2, nr)) { + if (!test_bit(FLG_PEER_BUSY, &l2->flag) && + (fi->state == ST_L2_7)) { + if (nr == l2->vs) { + stop_t200(l2, 13); + mISDN_FsmRestartTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 7); + } else if (nr != l2->va) + restart_t200(l2, 14); + } + setva(l2, nr); + } else { + nrerrorrecovery(fi); + return; + } + if (skb_queue_len(&l2->i_queue) && (fi->state == ST_L2_7)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag)) + enquiry_cr(l2, RR, RSP, 0); +} + +static void +l2_got_tei(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + u_int info; + + l2->tei = (signed char)(long)arg; + set_channel_address(&l2->ch, l2->sapi, l2->tei); + info = DL_INFO_L2_CONNECT; + l2up_create(l2, DL_INFORMATION_IND, sizeof(info), &info); + if (fi->state == ST_L2_3) { + establishlink(fi); + test_and_set_bit(FLG_L3_INIT, &l2->flag); + } else + mISDN_FsmChangeState(fi, ST_L2_4); + if (skb_queue_len(&l2->ui_queue)) + tx_ui(l2); +} + +static void +l2_st5_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + } else if (l2->rc == l2->N200) { + mISDN_FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + skb_queue_purge(&l2->i_queue); + l2mgr(l2, MDL_ERROR_IND, (void *) 'G'); + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, + l2_newid(l2), 0, NULL); + st5_dl_release_l2l3(l2); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } else { + l2->rc++; + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ? + SABME : SABM) | 0x10, CMD); + } +} + +static void +l2_st6_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + } else if (l2->rc == l2->N200) { + mISDN_FsmChangeState(fi, ST_L2_4); + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + l2mgr(l2, MDL_ERROR_IND, (void *) 'H'); + lapb_dl_release_l2l3(l2, DL_RELEASE_CNF); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + } else { + l2->rc++; + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, + NULL, 9); + send_uframe(l2, NULL, DISC | 0x10, CMD); + } +} + +static void +l2_st7_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + l2->rc = 0; + mISDN_FsmChangeState(fi, ST_L2_8); + transmit_enquiry(l2); + l2->rc++; +} + +static void +l2_st8_tout_200(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9); + return; + } + test_and_clear_bit(FLG_T200_RUN, &l2->flag); + if (l2->rc == l2->N200) { + l2mgr(l2, MDL_ERROR_IND, (void *) 'I'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } else { + transmit_enquiry(l2); + l2->rc++; + } +} + +static void +l2_st7_tout_203(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + if (test_bit(FLG_LAPD, &l2->flag) && + test_bit(FLG_DCHAN_BUSY, &l2->flag)) { + mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9); + return; + } + mISDN_FsmChangeState(fi, ST_L2_8); + transmit_enquiry(l2); + l2->rc = 0; +} + +static void +l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb, *nskb, *oskb; + u_char header[MAX_L2HEADER_LEN]; + u_int i, p1; + + if (!cansend(l2)) + return; + + skb = skb_dequeue(&l2->i_queue); + if (!skb) + return; + + if (test_bit(FLG_MOD128, &l2->flag)) + p1 = (l2->vs - l2->va) % 128; + else + p1 = (l2->vs - l2->va) % 8; + p1 = (p1 + l2->sow) % l2->window; + if (l2->windowar[p1]) { + printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n", + p1); + dev_kfree_skb(l2->windowar[p1]); + } + l2->windowar[p1] = skb; + i = sethdraddr(l2, header, CMD); + if (test_bit(FLG_MOD128, &l2->flag)) { + header[i++] = l2->vs << 1; + header[i++] = l2->vr << 1; + l2->vs = (l2->vs + 1) % 128; + } else { + header[i++] = (l2->vr << 5) | (l2->vs << 1); + l2->vs = (l2->vs + 1) % 8; + } + + nskb = skb_clone(skb, GFP_ATOMIC); + p1 = skb_headroom(nskb); + if (p1 >= i) + memcpy(skb_push(nskb, i), header, i); + else { + printk(KERN_WARNING + "isdnl2 pull_iqueue skb header(%d/%d) too short\n", i, p1); + oskb = nskb; + nskb = mI_alloc_skb(oskb->len + i, GFP_ATOMIC); + if (!nskb) { + dev_kfree_skb(oskb); + printk(KERN_WARNING "%s: no skb mem\n", __func__); + return; + } + memcpy(skb_put(nskb, i), header, i); + memcpy(skb_put(nskb, oskb->len), oskb->data, oskb->len); + dev_kfree_skb(oskb); + } + l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) { + mISDN_FsmDelTimer(&l2->t203, 13); + mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11); + } +} + +static void +l2_st8_got_super(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + int PollFlag, rsp, rnr = 0; + unsigned int nr; + + rsp = *skb->data & 0x2; + if (test_bit(FLG_ORIG, &l2->flag)) + rsp = !rsp; + + skb_pull(skb, l2addrsize(l2)); + + if (IsRNR(skb->data, l2)) { + set_peer_busy(l2); + rnr = 1; + } else + clear_peer_busy(l2); + + if (test_bit(FLG_MOD128, &l2->flag)) { + PollFlag = (skb->data[1] & 0x1) == 0x1; + nr = skb->data[1] >> 1; + } else { + PollFlag = (skb->data[0] & 0x10); + nr = (skb->data[0] >> 5) & 0x7; + } + dev_kfree_skb(skb); + if (rsp && PollFlag) { + if (legalnr(l2, nr)) { + if (rnr) { + restart_t200(l2, 15); + } else { + stop_t200(l2, 16); + mISDN_FsmAddTimer(&l2->t203, l2->T203, + EV_L2_T203, NULL, 5); + setva(l2, nr); + } + invoke_retransmission(l2, nr); + mISDN_FsmChangeState(fi, ST_L2_7); + if (skb_queue_len(&l2->i_queue) && cansend(l2)) + mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL); + } else + nrerrorrecovery(fi); + } else { + if (!rsp && PollFlag) + enquiry_response(l2); + if (legalnr(l2, nr)) + setva(l2, nr); + else + nrerrorrecovery(fi); + } +} + +static void +l2_got_FRMR(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_pull(skb, l2addrsize(l2) + 1); + + if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */ + (IsUA(skb->data) && (fi->state == ST_L2_7))) { + l2mgr(l2, MDL_ERROR_IND, (void *) 'K'); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); + } + dev_kfree_skb(skb); +} + +static void +l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->ui_queue); + l2->tei = GROUP_TEI; + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->ui_queue); + l2->tei = GROUP_TEI; + l2up_create(l2, DL_RELEASE_IND, 0, NULL); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + l2->tei = GROUP_TEI; + stop_t200(l2, 17); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->ui_queue); + l2->tei = GROUP_TEI; + stop_t200(l2, 18); + l2up_create(l2, DL_RELEASE_IND, 0, NULL); + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_tei_remove(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + l2->tei = GROUP_TEI; + stop_t200(l2, 17); + mISDN_FsmDelTimer(&l2->t203, 19); + l2up_create(l2, DL_RELEASE_IND, 0, NULL); +/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST, + * MGR_SHORTSTATUS_IND, SSTATUS_L2_RELEASED, + * 0, NULL, 0); + */ + mISDN_FsmChangeState(fi, ST_L2_1); +} + +static void +l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) + l2up(l2, DL_RELEASE_IND, skb); + else + dev_kfree_skb(skb); +} + +static void +l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + stop_t200(l2, 19); + st5_dl_release_l2l3(l2); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); + dev_kfree_skb(skb); +} + +static void +l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->ui_queue); + stop_t200(l2, 20); + l2up(l2, DL_RELEASE_CNF, skb); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_persistant_da(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + freewin(l2); + stop_t200(l2, 19); + mISDN_FsmDelTimer(&l2->t203, 19); + l2up(l2, DL_RELEASE_IND, skb); + mISDN_FsmChangeState(fi, ST_L2_4); + if (l2->tm) + l2_tei(l2, MDL_STATUS_DOWN_IND, 0); +} + +static void +l2_set_own_busy(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) { + enquiry_cr(l2, RNR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_clear_own_busy(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + struct sk_buff *skb = arg; + + if (!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) { + enquiry_cr(l2, RR, RSP, 0); + test_and_clear_bit(FLG_ACK_PEND, &l2->flag); + } + if (skb) + dev_kfree_skb(skb); +} + +static void +l2_frame_error(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR_IND, arg); +} + +static void +l2_frame_error_reest(struct FsmInst *fi, int event, void *arg) +{ + struct layer2 *l2 = fi->userdata; + + l2mgr(l2, MDL_ERROR_IND, arg); + establishlink(fi); + test_and_clear_bit(FLG_L3_INIT, &l2->flag); +} + +static struct FsmNode L2FnList[] = +{ + {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign}, + {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3}, + {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish}, + {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3}, + {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish}, + {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release}, + {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel}, + {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect}, + {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest}, + {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull}, + {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue}, + {ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign}, + {ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui}, + {ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui}, + {ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui}, + {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei}, + {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove}, + {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove}, + {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove}, + {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove}, + {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove}, + {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove}, + {ST_L2_4, EV_L2_SABME, l2_start_multi}, + {ST_L2_5, EV_L2_SABME, l2_send_UA}, + {ST_L2_6, EV_L2_SABME, l2_send_DM}, + {ST_L2_7, EV_L2_SABME, l2_restart_multi}, + {ST_L2_8, EV_L2_SABME, l2_restart_multi}, + {ST_L2_4, EV_L2_DISC, l2_send_DM}, + {ST_L2_5, EV_L2_DISC, l2_send_DM}, + {ST_L2_6, EV_L2_DISC, l2_send_UA}, + {ST_L2_7, EV_L2_DISC, l2_stop_multi}, + {ST_L2_8, EV_L2_DISC, l2_stop_multi}, + {ST_L2_4, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_5, EV_L2_UA, l2_connected}, + {ST_L2_6, EV_L2_UA, l2_released}, + {ST_L2_7, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_8, EV_L2_UA, l2_mdl_error_ua}, + {ST_L2_4, EV_L2_DM, l2_reestablish}, + {ST_L2_5, EV_L2_DM, l2_st5_dm_release}, + {ST_L2_6, EV_L2_DM, l2_st6_dm_release}, + {ST_L2_7, EV_L2_DM, l2_mdl_error_dm}, + {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm}, + {ST_L2_1, EV_L2_UI, l2_got_ui}, + {ST_L2_2, EV_L2_UI, l2_got_ui}, + {ST_L2_3, EV_L2_UI, l2_got_ui}, + {ST_L2_4, EV_L2_UI, l2_got_ui}, + {ST_L2_5, EV_L2_UI, l2_got_ui}, + {ST_L2_6, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_UI, l2_got_ui}, + {ST_L2_8, EV_L2_UI, l2_got_ui}, + {ST_L2_7, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_8, EV_L2_FRMR, l2_got_FRMR}, + {ST_L2_7, EV_L2_SUPER, l2_st7_got_super}, + {ST_L2_8, EV_L2_SUPER, l2_st8_got_super}, + {ST_L2_7, EV_L2_I, l2_got_iframe}, + {ST_L2_8, EV_L2_I, l2_got_iframe}, + {ST_L2_5, EV_L2_T200, l2_st5_tout_200}, + {ST_L2_6, EV_L2_T200, l2_st6_tout_200}, + {ST_L2_7, EV_L2_T200, l2_st7_tout_200}, + {ST_L2_8, EV_L2_T200, l2_st8_tout_200}, + {ST_L2_7, EV_L2_T203, l2_st7_tout_203}, + {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue}, + {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy}, + {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy}, + {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error}, + {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest}, + {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove}, + {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove}, + {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da}, + {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da}, + {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da}, + {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da}, + {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da}, +}; + +#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode)) + +static int +ph_data_indication(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) +{ + u_char *datap = skb->data; + int ret = -EINVAL; + int psapi, ptei; + u_int l; + int c = 0; + + l = l2addrsize(l2); + if (skb->len <= l) { + mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N'); + return ret; + } + if (test_bit(FLG_LAPD, &l2->flag)) { /* Maybe not needed */ + psapi = *datap++; + ptei = *datap++; + if ((psapi & 1) || !(ptei & 1)) { + printk(KERN_WARNING + "l2 D-channel frame wrong EA0/EA1\n"); + return ret; + } + psapi >>= 2; + ptei >>= 1; + if (psapi != l2->sapi) { + /* not our bussiness + * printk(KERN_DEBUG "%s: sapi %d/%d sapi mismatch\n", + * __func__, + * psapi, l2->sapi); + */ + dev_kfree_skb(skb); + return 0; + } + if ((ptei != l2->tei) && (ptei != GROUP_TEI)) { + /* not our bussiness + * printk(KERN_DEBUG "%s: tei %d/%d sapi %d mismatch\n", + * __func__, + * ptei, l2->tei, psapi); + */ + dev_kfree_skb(skb); + return 0; + } + } else + datap += l; + if (!(*datap & 1)) { /* I-Frame */ + c = iframe_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb); + } else if (IsSFrame(datap, l2)) { /* S-Frame */ + c = super_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb); + } else if (IsUI(datap)) { + c = UI_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb); + } else if (IsSABME(datap, l2)) { + c = unnum_error(l2, skb, CMD); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb); + } else if (IsUA(datap)) { + c = unnum_error(l2, skb, RSP); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb); + } else if (IsDISC(datap)) { + c = unnum_error(l2, skb, CMD); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb); + } else if (IsDM(datap)) { + c = unnum_error(l2, skb, RSP); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb); + } else if (IsFRMR(datap)) { + c = FRMR_error(l2, skb); + if (!c) + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_FRMR, skb); + } else + c = 'L'; + if (c) { + printk(KERN_WARNING "l2 D-channel frame error %c\n", c); + mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c); + } + return ret; +} + +static int +l2_send(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct layer2 *l2 = container_of(ch, struct layer2, ch); + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + + if (*debug & DEBUG_L2_RECV) + printk(KERN_DEBUG "%s: prim(%x) id(%x) tei(%d)\n", + __func__, hh->prim, hh->id, l2->tei); + switch (hh->prim) { + case PH_DATA_IND: + ret = ph_data_indication(l2, hh, skb); + break; + case PH_DATA_CNF: + ret = ph_data_confirm(l2, hh, skb); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_L1_ACTIV, &l2->flag); + l2up_create(l2, MPH_ACTIVATE_IND, 0, NULL); + if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag)) + ret = mISDN_FsmEvent(&l2->l2m, + EV_L2_DL_ESTABLISH_REQ, skb); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_L1_ACTIV, &l2->flag); + l2up_create(l2, MPH_DEACTIVATE_IND, 0, NULL); + ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, skb); + break; + case MPH_INFORMATION_IND: + if (!l2->up) + break; + ret = l2->up->send(l2->up, skb); + break; + case DL_DATA_REQ: + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb); + break; + case DL_UNITDATA_REQ: + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb); + break; + case DL_ESTABLISH_REQ: + if (test_bit(FLG_LAPB, &l2->flag)) + test_and_set_bit(FLG_ORIG, &l2->flag); + if (test_bit(FLG_L1_ACTIV, &l2->flag)) { + if (test_bit(FLG_LAPD, &l2->flag) || + test_bit(FLG_ORIG, &l2->flag)) + ret = mISDN_FsmEvent(&l2->l2m, + EV_L2_DL_ESTABLISH_REQ, skb); + } else { + if (test_bit(FLG_LAPD, &l2->flag) || + test_bit(FLG_ORIG, &l2->flag)) { + test_and_set_bit(FLG_ESTAB_PEND, + &l2->flag); + } + ret = l2down(l2, PH_ACTIVATE_REQ, l2_newid(l2), + skb); + } + break; + case DL_RELEASE_REQ: + if (test_bit(FLG_LAPB, &l2->flag)) + l2down_create(l2, PH_DEACTIVATE_REQ, + l2_newid(l2), 0, NULL); + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ, + skb); + break; + default: + if (*debug & DEBUG_L2) + l2m_debug(&l2->l2m, "l2 unknown pr %04x", + hh->prim); + } + if (ret) { + dev_kfree_skb(skb); + ret = 0; + } + return ret; +} + +int +tei_l2(struct layer2 *l2, u_int cmd, u_long arg) +{ + int ret = -EINVAL; + + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd); + switch (cmd) { + case (MDL_ASSIGN_REQ): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, (void *)arg); + break; + case (MDL_REMOVE_REQ): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, NULL); + break; + case (MDL_ERROR_IND): + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL); + break; + case (MDL_ERROR_RSP): + /* ETS 300-125 5.3.2.1 Test: TC13010 */ + printk(KERN_NOTICE "MDL_ERROR|REQ (tei_l2)\n"); + ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL); + break; + } + return ret; +} + +static void +release_l2(struct layer2 *l2) +{ + mISDN_FsmDelTimer(&l2->t200, 21); + mISDN_FsmDelTimer(&l2->t203, 16); + skb_queue_purge(&l2->i_queue); + skb_queue_purge(&l2->ui_queue); + skb_queue_purge(&l2->down_queue); + ReleaseWin(l2); + if (test_bit(FLG_LAPD, &l2->flag)) { + release_tei(l2); + if (l2->ch.st) + l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, + CLOSE_CHANNEL, NULL); + } + kfree(l2); +} + +static int +l2_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct layer2 *l2 = container_of(ch, struct layer2, ch); + u_int info; + + if (*debug & DEBUG_L2_CTRL) + printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd); + + switch (cmd) { + case OPEN_CHANNEL: + if (test_bit(FLG_LAPD, &l2->flag)) { + set_channel_address(&l2->ch, l2->sapi, l2->tei); + info = DL_INFO_L2_CONNECT; + l2up_create(l2, DL_INFORMATION_IND, + sizeof(info), &info); + } + break; + case CLOSE_CHANNEL: + if (l2->ch.peer) + l2->ch.peer->ctrl(l2->ch.peer, CLOSE_CHANNEL, NULL); + release_l2(l2); + break; + } + return 0; +} + +struct layer2 * +create_l2(struct mISDNchannel *ch, u_int protocol, u_long options, u_long arg) +{ + struct layer2 *l2; + struct channel_req rq; + + l2 = kzalloc(sizeof(struct layer2), GFP_KERNEL); + if (!l2) { + printk(KERN_ERR "kzalloc layer2 failed\n"); + return NULL; + } + l2->next_id = 1; + l2->down_id = MISDN_ID_NONE; + l2->up = ch; + l2->ch.st = ch->st; + l2->ch.send = l2_send; + l2->ch.ctrl = l2_ctrl; + switch (protocol) { + case ISDN_P_LAPD_NT: + test_and_set_bit(FLG_LAPD, &l2->flag); + test_and_set_bit(FLG_LAPD_NET, &l2->flag); + test_and_set_bit(FLG_MOD128, &l2->flag); + l2->sapi = 0; + l2->maxlen = MAX_DFRAME_LEN; + if (test_bit(OPTION_L2_PMX, &options)) + l2->window = 7; + else + l2->window = 1; + if (test_bit(OPTION_L2_PTP, &options)) + test_and_set_bit(FLG_PTP, &l2->flag); + if (test_bit(OPTION_L2_FIXEDTEI, &options)) + test_and_set_bit(FLG_FIXED_TEI, &l2->flag); + l2->tei = (u_int)arg; + l2->T200 = 1000; + l2->N200 = 3; + l2->T203 = 10000; + if (test_bit(OPTION_L2_PMX, &options)) + rq.protocol = ISDN_P_NT_E1; + else + rq.protocol = ISDN_P_NT_S0; + rq.adr.channel = 0; + l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq); + break; + case ISDN_P_LAPD_TE: + test_and_set_bit(FLG_LAPD, &l2->flag); + test_and_set_bit(FLG_MOD128, &l2->flag); + test_and_set_bit(FLG_ORIG, &l2->flag); + l2->sapi = 0; + l2->maxlen = MAX_DFRAME_LEN; + if (test_bit(OPTION_L2_PMX, &options)) + l2->window = 7; + else + l2->window = 1; + if (test_bit(OPTION_L2_PTP, &options)) + test_and_set_bit(FLG_PTP, &l2->flag); + if (test_bit(OPTION_L2_FIXEDTEI, &options)) + test_and_set_bit(FLG_FIXED_TEI, &l2->flag); + l2->tei = (u_int)arg; + l2->T200 = 1000; + l2->N200 = 3; + l2->T203 = 10000; + if (test_bit(OPTION_L2_PMX, &options)) + rq.protocol = ISDN_P_TE_E1; + else + rq.protocol = ISDN_P_TE_S0; + rq.adr.channel = 0; + l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq); + break; + case ISDN_P_B_X75SLP: + test_and_set_bit(FLG_LAPB, &l2->flag); + l2->window = 7; + l2->maxlen = MAX_DATA_SIZE; + l2->T200 = 1000; + l2->N200 = 4; + l2->T203 = 5000; + l2->addr.A = 3; + l2->addr.B = 1; + break; + default: + printk(KERN_ERR "layer2 create failed prt %x\n", + protocol); + kfree(l2); + return NULL; + } + skb_queue_head_init(&l2->i_queue); + skb_queue_head_init(&l2->ui_queue); + skb_queue_head_init(&l2->down_queue); + skb_queue_head_init(&l2->tmp_queue); + InitWin(l2); + l2->l2m.fsm = &l2fsm; + if (test_bit(FLG_LAPB, &l2->flag) || + test_bit(FLG_PTP, &l2->flag) || + test_bit(FLG_LAPD_NET, &l2->flag)) + l2->l2m.state = ST_L2_4; + else + l2->l2m.state = ST_L2_1; + l2->l2m.debug = *debug; + l2->l2m.userdata = l2; + l2->l2m.userint = 0; + l2->l2m.printdebug = l2m_debug; + + mISDN_FsmInitTimer(&l2->l2m, &l2->t200); + mISDN_FsmInitTimer(&l2->l2m, &l2->t203); + return l2; +} + +static int +x75create(struct channel_req *crq) +{ + struct layer2 *l2; + + if (crq->protocol != ISDN_P_B_X75SLP) + return -EPROTONOSUPPORT; + l2 = create_l2(crq->ch, crq->protocol, 0, 0); + if (!l2) + return -ENOMEM; + crq->ch = &l2->ch; + crq->protocol = ISDN_P_B_HDLC; + return 0; +} + +static struct Bprotocol X75SLP = { + .Bprotocols = (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK)), + .name = "X75SLP", + .create = x75create +}; + +int +Isdnl2_Init(u_int *deb) +{ + debug = deb; + mISDN_register_Bprotocol(&X75SLP); + l2fsm.state_count = L2_STATE_COUNT; + l2fsm.event_count = L2_EVENT_COUNT; + l2fsm.strEvent = strL2Event; + l2fsm.strState = strL2State; + mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList)); + TEIInit(deb); + return 0; +} + +void +Isdnl2_cleanup(void) +{ + mISDN_unregister_Bprotocol(&X75SLP); + TEIFree(); + mISDN_FsmFree(&l2fsm); +} + diff --git a/drivers/isdn/mISDN/layer2.h b/drivers/isdn/mISDN/layer2.h new file mode 100644 index 000000000000..de2dd02056a3 --- /dev/null +++ b/drivers/isdn/mISDN/layer2.h @@ -0,0 +1,140 @@ +/* + * Layer 2 defines + * + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "fsm.h" + +#define MAX_WINDOW 8 + +struct manager { + struct mISDNchannel ch; + struct mISDNchannel bcast; + u_long options; + struct list_head layer2; + rwlock_t lock; + struct FsmInst deact; + struct FsmTimer datimer; + struct sk_buff_head sendq; + struct mISDNchannel *up; + u_int nextid; + u_int lastid; +}; + +struct teimgr { + int ri; + int rcnt; + struct FsmInst tei_m; + struct FsmTimer timer; + int tval, nval; + struct layer2 *l2; + struct manager *mgr; +}; + +struct laddr { + u_char A; + u_char B; +}; + +struct layer2 { + struct list_head list; + struct mISDNchannel ch; + u_long flag; + int id; + struct mISDNchannel *up; + signed char sapi; + signed char tei; + struct laddr addr; + u_int maxlen; + struct teimgr *tm; + u_int vs, va, vr; + int rc; + u_int window; + u_int sow; + struct FsmInst l2m; + struct FsmTimer t200, t203; + int T200, N200, T203; + u_int next_id; + u_int down_id; + struct sk_buff *windowar[MAX_WINDOW]; + struct sk_buff_head i_queue; + struct sk_buff_head ui_queue; + struct sk_buff_head down_queue; + struct sk_buff_head tmp_queue; +}; + +enum { + ST_L2_1, + ST_L2_2, + ST_L2_3, + ST_L2_4, + ST_L2_5, + ST_L2_6, + ST_L2_7, + ST_L2_8, +}; + +#define L2_STATE_COUNT (ST_L2_8+1) + +extern struct layer2 *create_l2(struct mISDNchannel *, u_int, + u_long, u_long); +extern int tei_l2(struct layer2 *, u_int, u_long arg); + + +/* from tei.c */ +extern int l2_tei(struct layer2 *, u_int, u_long arg); +extern void release_tei(struct layer2 *); +extern int TEIInit(u_int *); +extern void TEIFree(void); + +#define MAX_L2HEADER_LEN 4 + +#define RR 0x01 +#define RNR 0x05 +#define REJ 0x09 +#define SABME 0x6f +#define SABM 0x2f +#define DM 0x0f +#define UI 0x03 +#define DISC 0x43 +#define UA 0x63 +#define FRMR 0x87 +#define XID 0xaf + +#define CMD 0 +#define RSP 1 + +#define LC_FLUSH_WAIT 1 + +#define FLG_LAPB 0 +#define FLG_LAPD 1 +#define FLG_ORIG 2 +#define FLG_MOD128 3 +#define FLG_PEND_REL 4 +#define FLG_L3_INIT 5 +#define FLG_T200_RUN 6 +#define FLG_ACK_PEND 7 +#define FLG_REJEXC 8 +#define FLG_OWN_BUSY 9 +#define FLG_PEER_BUSY 10 +#define FLG_DCHAN_BUSY 11 +#define FLG_L1_ACTIV 12 +#define FLG_ESTAB_PEND 13 +#define FLG_PTP 14 +#define FLG_FIXED_TEI 15 +#define FLG_L2BLOCK 16 +#define FLG_L1_NOTREADY 17 +#define FLG_LAPD_NET 18 diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c new file mode 100644 index 000000000000..4ba4cc364c9e --- /dev/null +++ b/drivers/isdn/mISDN/socket.c @@ -0,0 +1,781 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include "core.h" + +static int *debug; + +static struct proto mISDN_proto = { + .name = "misdn", + .owner = THIS_MODULE, + .obj_size = sizeof(struct mISDN_sock) +}; + +#define _pms(sk) ((struct mISDN_sock *)sk) + +static struct mISDN_sock_list data_sockets = { + .lock = __RW_LOCK_UNLOCKED(data_sockets.lock) +}; + +static struct mISDN_sock_list base_sockets = { + .lock = __RW_LOCK_UNLOCKED(base_sockets.lock) +}; + +#define L2_HEADER_LEN 4 + +static inline struct sk_buff * +_l2_alloc_skb(unsigned int len, gfp_t gfp_mask) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask); + if (likely(skb)) + skb_reserve(skb, L2_HEADER_LEN); + return skb; +} + +static void +mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk) +{ + write_lock_bh(&l->lock); + sk_add_node(sk, &l->head); + write_unlock_bh(&l->lock); +} + +static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk) +{ + write_lock_bh(&l->lock); + sk_del_node_init(sk); + write_unlock_bh(&l->lock); +} + +static int +mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDN_sock *msk; + int err; + + msk = container_of(ch, struct mISDN_sock, ch); + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb); + if (msk->sk.sk_state == MISDN_CLOSED) + return -EUNATCH; + __net_timestamp(skb); + err = sock_queue_rcv_skb(&msk->sk, skb); + if (err) + printk(KERN_WARNING "%s: error %d\n", __func__, err); + return err; +} + +static int +mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDN_sock *msk; + + msk = container_of(ch, struct mISDN_sock, ch); + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + msk->sk.sk_state = MISDN_CLOSED; + break; + } + return 0; +} + +static inline void +mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) +{ + struct timeval tv; + + if (_pms(sk)->cmask & MISDN_TIME_STAMP) { + skb_get_timestamp(skb, &tv); + put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv); + } +} + +static int +mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + struct sk_buff *skb; + struct sock *sk = sock->sk; + struct sockaddr_mISDN *maddr; + + int copied, err; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n", + __func__, (int)len, flags, _pms(sk)->ch.nr, + sk->sk_protocol); + if (flags & (MSG_OOB)) + return -EOPNOTSUPP; + + if (sk->sk_state == MISDN_CLOSED) + return 0; + + skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err); + if (!skb) + return err; + + if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) { + msg->msg_namelen = sizeof(struct sockaddr_mISDN); + maddr = (struct sockaddr_mISDN *)msg->msg_name; + maddr->family = AF_ISDN; + maddr->dev = _pms(sk)->dev->id; + if ((sk->sk_protocol == ISDN_P_LAPD_TE) || + (sk->sk_protocol == ISDN_P_LAPD_NT)) { + maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff; + maddr->tei = (mISDN_HEAD_ID(skb) >> 8) & 0xff; + maddr->sapi = mISDN_HEAD_ID(skb) & 0xff; + } else { + maddr->channel = _pms(sk)->ch.nr; + maddr->sapi = _pms(sk)->ch.addr & 0xFF; + maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xFF; + } + } else { + if (msg->msg_namelen) + printk(KERN_WARNING "%s: too small namelen %d\n", + __func__, msg->msg_namelen); + msg->msg_namelen = 0; + } + + copied = skb->len + MISDN_HEADER_LEN; + if (len < copied) { + if (flags & MSG_PEEK) + atomic_dec(&skb->users); + else + skb_queue_head(&sk->sk_receive_queue, skb); + return -ENOSPC; + } + memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb), + MISDN_HEADER_LEN); + + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + + mISDN_sock_cmsg(sk, msg, skb); + + skb_free_datagram(sk, skb); + + return err ? : copied; +} + +static int +mISDN_sock_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct sk_buff *skb; + int err = -ENOMEM; + struct sockaddr_mISDN *maddr; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n", + __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr, + sk->sk_protocol); + + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) + return -EINVAL; + + if (len < MISDN_HEADER_LEN) + return -EINVAL; + + if (sk->sk_state != MISDN_BOUND) + return -EBADFD; + + lock_sock(sk); + + skb = _l2_alloc_skb(len, GFP_KERNEL); + if (!skb) + goto done; + + if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { + err = -EFAULT; + goto drop; + } + + memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN); + skb_pull(skb, MISDN_HEADER_LEN); + + if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) { + /* if we have a address, we use it */ + maddr = (struct sockaddr_mISDN *)msg->msg_name; + mISDN_HEAD_ID(skb) = maddr->channel; + } else { /* use default for L2 messages */ + if ((sk->sk_protocol == ISDN_P_LAPD_TE) || + (sk->sk_protocol == ISDN_P_LAPD_NT)) + mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr; + } + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s: ID:%x\n", + __func__, mISDN_HEAD_ID(skb)); + + err = -ENODEV; + if (!_pms(sk)->ch.peer || + (err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb))) + goto drop; + + err = len; + +done: + release_sock(sk); + return err; + +drop: + kfree_skb(skb); + goto done; +} + +static int +data_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); + if (!sk) + return 0; + switch (sk->sk_protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + if (sk->sk_state == MISDN_BOUND) + delete_channel(&_pms(sk)->ch); + else + mISDN_sock_unlink(&data_sockets, sk); + break; + case ISDN_P_LAPD_TE: + case ISDN_P_LAPD_NT: + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + case ISDN_P_B_X75SLP: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_L2DSP: + case ISDN_P_B_L2DSPHDLC: + delete_channel(&_pms(sk)->ch); + mISDN_sock_unlink(&data_sockets, sk); + break; + } + + lock_sock(sk); + + sock_orphan(sk); + skb_queue_purge(&sk->sk_receive_queue); + + release_sock(sk); + sock_put(sk); + + return 0; +} + +static int +data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p) +{ + struct mISDN_ctrl_req cq; + int err = -EINVAL, val; + struct mISDNchannel *bchan, *next; + + lock_sock(sk); + if (!_pms(sk)->dev) { + err = -ENODEV; + goto done; + } + switch (cmd) { + case IMCTRLREQ: + if (copy_from_user(&cq, p, sizeof(cq))) { + err = -EFAULT; + break; + } + if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) { + list_for_each_entry_safe(bchan, next, + &_pms(sk)->dev->bchannels, list) { + if (bchan->nr == cq.channel) { + err = bchan->ctrl(bchan, + CONTROL_CHANNEL, &cq); + break; + } + } + } else + err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D, + CONTROL_CHANNEL, &cq); + if (err) + break; + if (copy_to_user(p, &cq, sizeof(cq))) + err = -EFAULT; + break; + case IMCLEAR_L2: + if (sk->sk_protocol != ISDN_P_LAPD_NT) { + err = -EINVAL; + break; + } + if (get_user(val, (int __user *)p)) { + err = -EFAULT; + break; + } + err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr, + CONTROL_CHANNEL, &val); + break; + default: + err = -EINVAL; + break; + } +done: + release_sock(sk); + return err; +} + +static int +data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + int err = 0, id; + struct sock *sk = sock->sk; + struct mISDNdevice *dev; + struct mISDNversion ver; + + switch (cmd) { + case IMGETVERSION: + ver.major = MISDN_MAJOR_VERSION; + ver.minor = MISDN_MINOR_VERSION; + ver.release = MISDN_RELEASE; + if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) + err = -EFAULT; + break; + case IMGETCOUNT: + id = get_mdevice_count(); + if (put_user(id, (int __user *)arg)) + err = -EFAULT; + break; + case IMGETDEVINFO: + if (get_user(id, (int __user *)arg)) { + err = -EFAULT; + break; + } + dev = get_mdevice(id); + if (dev) { + struct mISDN_devinfo di; + + di.id = dev->id; + di.Dprotocols = dev->Dprotocols; + di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); + di.protocol = dev->D.protocol; + memcpy(di.channelmap, dev->channelmap, + MISDN_CHMAP_SIZE * 4); + di.nrbchan = dev->nrbchan; + strcpy(di.name, dev->name); + if (copy_to_user((void __user *)arg, &di, sizeof(di))) + err = -EFAULT; + } else + err = -ENODEV; + break; + default: + if (sk->sk_state == MISDN_BOUND) + err = data_sock_ioctl_bound(sk, cmd, + (void __user *)arg); + else + err = -ENOTCONN; + } + return err; +} + +static int data_sock_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, int len) +{ + struct sock *sk = sock->sk; + int err = 0, opt = 0; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p, %d, %x, %p, %d)\n", __func__, sock, + level, optname, optval, len); + + lock_sock(sk); + + switch (optname) { + case MISDN_TIME_STAMP: + if (get_user(opt, (int __user *)optval)) { + err = -EFAULT; + break; + } + + if (opt) + _pms(sk)->cmask |= MISDN_TIME_STAMP; + else + _pms(sk)->cmask &= ~MISDN_TIME_STAMP; + break; + default: + err = -ENOPROTOOPT; + break; + } + release_sock(sk); + return err; +} + +static int data_sock_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + int len, opt; + + if (get_user(len, optlen)) + return -EFAULT; + + switch (optname) { + case MISDN_TIME_STAMP: + if (_pms(sk)->cmask & MISDN_TIME_STAMP) + opt = 1; + else + opt = 0; + + if (put_user(opt, optval)) + return -EFAULT; + break; + default: + return -ENOPROTOOPT; + } + + return 0; +} + +static int +data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +{ + struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; + struct sock *sk = sock->sk; + int err = 0; + + if (*debug & DEBUG_SOCKET) + printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); + if (addr_len != sizeof(struct sockaddr_mISDN)) + return -EINVAL; + if (!maddr || maddr->family != AF_ISDN) + return -EINVAL; + + lock_sock(sk); + + if (_pms(sk)->dev) { + err = -EALREADY; + goto done; + } + _pms(sk)->dev = get_mdevice(maddr->dev); + if (!_pms(sk)->dev) { + err = -ENODEV; + goto done; + } + _pms(sk)->ch.send = mISDN_send; + _pms(sk)->ch.ctrl = mISDN_ctrl; + + switch (sk->sk_protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + mISDN_sock_unlink(&data_sockets, sk); + err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch, + sk->sk_protocol, maddr); + if (err) + mISDN_sock_link(&data_sockets, sk); + break; + case ISDN_P_LAPD_TE: + case ISDN_P_LAPD_NT: + err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch, + sk->sk_protocol, maddr); + break; + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + case ISDN_P_B_X75SLP: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_L2DSP: + case ISDN_P_B_L2DSPHDLC: + err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch, + sk->sk_protocol, maddr); + break; + default: + err = -EPROTONOSUPPORT; + } + if (err) + goto done; + sk->sk_state = MISDN_BOUND; + _pms(sk)->ch.protocol = sk->sk_protocol; + +done: + release_sock(sk); + return err; +} + +static int +data_sock_getname(struct socket *sock, struct sockaddr *addr, + int *addr_len, int peer) +{ + struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; + struct sock *sk = sock->sk; + + if (!_pms(sk)->dev) + return -EBADFD; + + lock_sock(sk); + + *addr_len = sizeof(*maddr); + maddr->dev = _pms(sk)->dev->id; + maddr->channel = _pms(sk)->ch.nr; + maddr->sapi = _pms(sk)->ch.addr & 0xff; + maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff; + release_sock(sk); + return 0; +} + +static const struct proto_ops data_sock_ops = { + .family = PF_ISDN, + .owner = THIS_MODULE, + .release = data_sock_release, + .ioctl = data_sock_ioctl, + .bind = data_sock_bind, + .getname = data_sock_getname, + .sendmsg = mISDN_sock_sendmsg, + .recvmsg = mISDN_sock_recvmsg, + .poll = datagram_poll, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = data_sock_setsockopt, + .getsockopt = data_sock_getsockopt, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .mmap = sock_no_mmap +}; + +static int +data_sock_create(struct net *net, struct socket *sock, int protocol) +{ + struct sock *sk; + + if (sock->type != SOCK_DGRAM) + return -ESOCKTNOSUPPORT; + + sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + + sock->ops = &data_sock_ops; + sock->state = SS_UNCONNECTED; + sock_reset_flag(sk, SOCK_ZAPPED); + + sk->sk_protocol = protocol; + sk->sk_state = MISDN_OPEN; + mISDN_sock_link(&data_sockets, sk); + + return 0; +} + +static int +base_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk); + if (!sk) + return 0; + + mISDN_sock_unlink(&base_sockets, sk); + sock_orphan(sk); + sock_put(sk); + + return 0; +} + +static int +base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + int err = 0, id; + struct mISDNdevice *dev; + struct mISDNversion ver; + + switch (cmd) { + case IMGETVERSION: + ver.major = MISDN_MAJOR_VERSION; + ver.minor = MISDN_MINOR_VERSION; + ver.release = MISDN_RELEASE; + if (copy_to_user((void __user *)arg, &ver, sizeof(ver))) + err = -EFAULT; + break; + case IMGETCOUNT: + id = get_mdevice_count(); + if (put_user(id, (int __user *)arg)) + err = -EFAULT; + break; + case IMGETDEVINFO: + if (get_user(id, (int __user *)arg)) { + err = -EFAULT; + break; + } + dev = get_mdevice(id); + if (dev) { + struct mISDN_devinfo di; + + di.id = dev->id; + di.Dprotocols = dev->Dprotocols; + di.Bprotocols = dev->Bprotocols | get_all_Bprotocols(); + di.protocol = dev->D.protocol; + memcpy(di.channelmap, dev->channelmap, + MISDN_CHMAP_SIZE * 4); + di.nrbchan = dev->nrbchan; + strcpy(di.name, dev->name); + if (copy_to_user((void __user *)arg, &di, sizeof(di))) + err = -EFAULT; + } else + err = -ENODEV; + break; + default: + err = -EINVAL; + } + return err; +} + +static int +base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +{ + struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; + struct sock *sk = sock->sk; + int err = 0; + + if (!maddr || maddr->family != AF_ISDN) + return -EINVAL; + + lock_sock(sk); + + if (_pms(sk)->dev) { + err = -EALREADY; + goto done; + } + + _pms(sk)->dev = get_mdevice(maddr->dev); + if (!_pms(sk)->dev) { + err = -ENODEV; + goto done; + } + sk->sk_state = MISDN_BOUND; + +done: + release_sock(sk); + return err; +} + +static const struct proto_ops base_sock_ops = { + .family = PF_ISDN, + .owner = THIS_MODULE, + .release = base_sock_release, + .ioctl = base_sock_ioctl, + .bind = base_sock_bind, + .getname = sock_no_getname, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .poll = sock_no_poll, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .mmap = sock_no_mmap +}; + + +static int +base_sock_create(struct net *net, struct socket *sock, int protocol) +{ + struct sock *sk; + + if (sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + + sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sock->ops = &base_sock_ops; + sock->state = SS_UNCONNECTED; + sock_reset_flag(sk, SOCK_ZAPPED); + sk->sk_protocol = protocol; + sk->sk_state = MISDN_OPEN; + mISDN_sock_link(&base_sockets, sk); + + return 0; +} + +static int +mISDN_sock_create(struct net *net, struct socket *sock, int proto) +{ + int err = -EPROTONOSUPPORT; + + switch (proto) { + case ISDN_P_BASE: + err = base_sock_create(net, sock, proto); + break; + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + case ISDN_P_LAPD_TE: + case ISDN_P_LAPD_NT: + case ISDN_P_B_RAW: + case ISDN_P_B_HDLC: + case ISDN_P_B_X75SLP: + case ISDN_P_B_L2DTMF: + case ISDN_P_B_L2DSP: + case ISDN_P_B_L2DSPHDLC: + err = data_sock_create(net, sock, proto); + break; + default: + return err; + } + + return err; +} + +static struct +net_proto_family mISDN_sock_family_ops = { + .owner = THIS_MODULE, + .family = PF_ISDN, + .create = mISDN_sock_create, +}; + +int +misdn_sock_init(u_int *deb) +{ + int err; + + debug = deb; + err = sock_register(&mISDN_sock_family_ops); + if (err) + printk(KERN_ERR "%s: error(%d)\n", __func__, err); + return err; +} + +void +misdn_sock_cleanup(void) +{ + sock_unregister(PF_ISDN); +} + diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c new file mode 100644 index 000000000000..54cfddcc4784 --- /dev/null +++ b/drivers/isdn/mISDN/stack.c @@ -0,0 +1,674 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include "core.h" + +static u_int *debug; + +static inline void +_queue_message(struct mISDNstack *st, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + + if (*debug & DEBUG_QUEUE_FUNC) + printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n", + __func__, hh->prim, hh->id, skb); + skb_queue_tail(&st->msgq, skb); + if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) { + test_and_set_bit(mISDN_STACK_WORK, &st->status); + wake_up_interruptible(&st->workq); + } +} + +int +mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb) +{ + _queue_message(ch->st, skb); + return 0; +} + +static struct mISDNchannel * +get_channel4id(struct mISDNstack *st, u_int id) +{ + struct mISDNchannel *ch; + + mutex_lock(&st->lmutex); + list_for_each_entry(ch, &st->layer2, list) { + if (id == ch->nr) + goto unlock; + } + ch = NULL; +unlock: + mutex_unlock(&st->lmutex); + return ch; +} + +static void +send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb) +{ + struct hlist_node *node; + struct sock *sk; + struct sk_buff *cskb = NULL; + + read_lock(&sl->lock); + sk_for_each(sk, node, &sl->head) { + if (sk->sk_state != MISDN_BOUND) + continue; + if (!cskb) + cskb = skb_copy(skb, GFP_KERNEL); + if (!cskb) { + printk(KERN_WARNING "%s no skb\n", __func__); + break; + } + if (!sock_queue_rcv_skb(sk, cskb)) + cskb = NULL; + } + read_unlock(&sl->lock); + if (cskb) + dev_kfree_skb(cskb); +} + +static void +send_layer2(struct mISDNstack *st, struct sk_buff *skb) +{ + struct sk_buff *cskb; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct mISDNchannel *ch; + int ret; + + if (!st) + return; + mutex_lock(&st->lmutex); + if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */ + list_for_each_entry(ch, &st->layer2, list) { + if (list_is_last(&ch->list, &st->layer2)) { + cskb = skb; + skb = NULL; + } else { + cskb = skb_copy(skb, GFP_KERNEL); + } + if (cskb) { + ret = ch->send(ch, cskb); + if (ret) { + if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s ch%d prim(%x) addr(%x)" + " err %d\n", + __func__, ch->nr, + hh->prim, ch->addr, ret); + dev_kfree_skb(cskb); + } + } else { + printk(KERN_WARNING "%s ch%d addr %x no mem\n", + __func__, ch->nr, ch->addr); + goto out; + } + } + } else { + list_for_each_entry(ch, &st->layer2, list) { + if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) { + ret = ch->send(ch, skb); + if (!ret) + skb = NULL; + goto out; + } + } + ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb); + if (!ret) + skb = NULL; + else if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s ch%d mgr prim(%x) addr(%x) err %d\n", + __func__, ch->nr, hh->prim, ch->addr, ret); + } +out: + mutex_unlock(&st->lmutex); + if (skb) + dev_kfree_skb(skb); +} + +static inline int +send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct mISDNchannel *ch; + int lm; + + lm = hh->prim & MISDN_LAYERMASK; + if (*debug & DEBUG_QUEUE_FUNC) + printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n", + __func__, hh->prim, hh->id, skb); + if (lm == 0x1) { + if (!hlist_empty(&st->l1sock.head)) { + __net_timestamp(skb); + send_socklist(&st->l1sock, skb); + } + return st->layer1->send(st->layer1, skb); + } else if (lm == 0x2) { + if (!hlist_empty(&st->l1sock.head)) + send_socklist(&st->l1sock, skb); + send_layer2(st, skb); + return 0; + } else if (lm == 0x4) { + ch = get_channel4id(st, hh->id); + if (ch) + return ch->send(ch, skb); + else + printk(KERN_WARNING + "%s: dev(%s) prim(%x) id(%x) no channel\n", + __func__, st->dev->name, hh->prim, hh->id); + } else if (lm == 0x8) { + WARN_ON(lm == 0x8); + ch = get_channel4id(st, hh->id); + if (ch) + return ch->send(ch, skb); + else + printk(KERN_WARNING + "%s: dev(%s) prim(%x) id(%x) no channel\n", + __func__, st->dev->name, hh->prim, hh->id); + } else { + /* broadcast not handled yet */ + printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n", + __func__, st->dev->name, hh->prim); + } + return -ESRCH; +} + +static void +do_clear_stack(struct mISDNstack *st) +{ +} + +static int +mISDNStackd(void *data) +{ + struct mISDNstack *st = data; + int err = 0; + +#ifdef CONFIG_SMP + lock_kernel(); +#endif + sigfillset(¤t->blocked); +#ifdef CONFIG_SMP + unlock_kernel(); +#endif + if (*debug & DEBUG_MSG_THREAD) + printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name); + + if (st->notify != NULL) { + complete(st->notify); + st->notify = NULL; + } + + for (;;) { + struct sk_buff *skb; + + if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) { + test_and_clear_bit(mISDN_STACK_WORK, &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); + } else + test_and_set_bit(mISDN_STACK_RUNNING, &st->status); + while (test_bit(mISDN_STACK_WORK, &st->status)) { + skb = skb_dequeue(&st->msgq); + if (!skb) { + test_and_clear_bit(mISDN_STACK_WORK, + &st->status); + /* test if a race happens */ + skb = skb_dequeue(&st->msgq); + if (!skb) + continue; + test_and_set_bit(mISDN_STACK_WORK, + &st->status); + } +#ifdef MISDN_MSG_STATS + st->msg_cnt++; +#endif + err = send_msg_to_layer(st, skb); + if (unlikely(err)) { + if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s: %s prim(%x) id(%x) " + "send call(%d)\n", + __func__, st->dev->name, + mISDN_HEAD_PRIM(skb), + mISDN_HEAD_ID(skb), err); + dev_kfree_skb(skb); + continue; + } + if (unlikely(test_bit(mISDN_STACK_STOPPED, + &st->status))) { + test_and_clear_bit(mISDN_STACK_WORK, + &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, + &st->status); + break; + } + } + if (test_bit(mISDN_STACK_CLEARING, &st->status)) { + test_and_set_bit(mISDN_STACK_STOPPED, &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); + do_clear_stack(st); + test_and_clear_bit(mISDN_STACK_CLEARING, &st->status); + test_and_set_bit(mISDN_STACK_RESTART, &st->status); + } + if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) { + test_and_clear_bit(mISDN_STACK_STOPPED, &st->status); + test_and_set_bit(mISDN_STACK_RUNNING, &st->status); + if (!skb_queue_empty(&st->msgq)) + test_and_set_bit(mISDN_STACK_WORK, + &st->status); + } + if (test_bit(mISDN_STACK_ABORT, &st->status)) + break; + if (st->notify != NULL) { + complete(st->notify); + st->notify = NULL; + } +#ifdef MISDN_MSG_STATS + st->sleep_cnt++; +#endif + test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); + wait_event_interruptible(st->workq, (st->status & + mISDN_STACK_ACTION_MASK)); + if (*debug & DEBUG_MSG_THREAD) + printk(KERN_DEBUG "%s: %s wake status %08lx\n", + __func__, st->dev->name, st->status); + test_and_set_bit(mISDN_STACK_ACTIVE, &st->status); + + test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status); + + if (test_bit(mISDN_STACK_STOPPED, &st->status)) { + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); +#ifdef MISDN_MSG_STATS + st->stopped_cnt++; +#endif + } + } +#ifdef MISDN_MSG_STATS + printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d " + "msg %d sleep %d stopped\n", + st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt); + printk(KERN_DEBUG + "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n", + st->dev->name, st->thread->utime, st->thread->stime); + printk(KERN_DEBUG + "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n", + st->dev->name, st->thread->nvcsw, st->thread->nivcsw); + printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n", + st->dev->name); +#endif + test_and_set_bit(mISDN_STACK_KILLED, &st->status); + test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); + test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); + test_and_clear_bit(mISDN_STACK_ABORT, &st->status); + skb_queue_purge(&st->msgq); + st->thread = NULL; + if (st->notify != NULL) { + complete(st->notify); + st->notify = NULL; + } + return 0; +} + +static int +l1_receive(struct mISDNchannel *ch, struct sk_buff *skb) +{ + if (!ch->st) + return -ENODEV; + __net_timestamp(skb); + _queue_message(ch->st, skb); + return 0; +} + +void +set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei) +{ + ch->addr = sapi | (tei << 8); +} + +void +__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) +{ + list_add_tail(&ch->list, &st->layer2); +} + +void +add_layer2(struct mISDNchannel *ch, struct mISDNstack *st) +{ + mutex_lock(&st->lmutex); + __add_layer2(ch, st); + mutex_unlock(&st->lmutex); +} + +static int +st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + if (!ch->st || ch->st->layer1) + return -EINVAL; + return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg); +} + +int +create_stack(struct mISDNdevice *dev) +{ + struct mISDNstack *newst; + int err; + DECLARE_COMPLETION_ONSTACK(done); + + newst = kzalloc(sizeof(struct mISDNstack), GFP_KERNEL); + if (!newst) { + printk(KERN_ERR "kmalloc mISDN_stack failed\n"); + return -ENOMEM; + } + newst->dev = dev; + INIT_LIST_HEAD(&newst->layer2); + INIT_HLIST_HEAD(&newst->l1sock.head); + rwlock_init(&newst->l1sock.lock); + init_waitqueue_head(&newst->workq); + skb_queue_head_init(&newst->msgq); + mutex_init(&newst->lmutex); + dev->D.st = newst; + err = create_teimanager(dev); + if (err) { + printk(KERN_ERR "kmalloc teimanager failed\n"); + kfree(newst); + return err; + } + dev->teimgr->peer = &newst->own; + dev->teimgr->recv = mISDN_queue_message; + dev->teimgr->st = newst; + newst->layer1 = &dev->D; + dev->D.recv = l1_receive; + dev->D.peer = &newst->own; + newst->own.st = newst; + newst->own.ctrl = st_own_ctrl; + newst->own.send = mISDN_queue_message; + newst->own.recv = mISDN_queue_message; + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%s)\n", __func__, newst->dev->name); + newst->notify = &done; + newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s", + newst->dev->name); + if (IS_ERR(newst->thread)) { + err = PTR_ERR(newst->thread); + printk(KERN_ERR + "mISDN:cannot create kernel thread for %s (%d)\n", + newst->dev->name, err); + delete_teimanager(dev->teimgr); + kfree(newst); + } else + wait_for_completion(&done); + return err; +} + +int +connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch, + u_int protocol, struct sockaddr_mISDN *adr) +{ + struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch); + struct channel_req rq; + int err; + + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, dev->name, protocol, adr->dev, adr->channel, + adr->sapi, adr->tei); + switch (protocol) { + case ISDN_P_NT_S0: + case ISDN_P_NT_E1: + case ISDN_P_TE_S0: + case ISDN_P_TE_E1: +#ifdef PROTOCOL_CHECK + /* this should be enhanced */ + if (!list_empty(&dev->D.st->layer2) + && dev->D.protocol != protocol) + return -EBUSY; + if (!hlist_empty(&dev->D.st->l1sock.head) + && dev->D.protocol != protocol) + return -EBUSY; +#endif + ch->recv = mISDN_queue_message; + ch->peer = &dev->D.st->own; + ch->st = dev->D.st; + rq.protocol = protocol; + rq.adr.channel = 0; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); + if (err) + return err; + write_lock_bh(&dev->D.st->l1sock.lock); + sk_add_node(&msk->sk, &dev->D.st->l1sock.head); + write_unlock_bh(&dev->D.st->l1sock.lock); + break; + default: + return -ENOPROTOOPT; + } + return 0; +} + +int +connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch, + u_int protocol, struct sockaddr_mISDN *adr) +{ + struct channel_req rq, rq2; + int pmask, err; + struct Bprotocol *bp; + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, dev->name, protocol, + adr->dev, adr->channel, adr->sapi, + adr->tei); + ch->st = dev->D.st; + pmask = 1 << (protocol & ISDN_P_B_MASK); + if (pmask & dev->Bprotocols) { + rq.protocol = protocol; + rq.adr = *adr; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + if (err) + return err; + ch->recv = rq.ch->send; + ch->peer = rq.ch; + rq.ch->recv = ch->send; + rq.ch->peer = ch; + rq.ch->st = dev->D.st; + } else { + bp = get_Bprotocol4mask(pmask); + if (!bp) + return -ENOPROTOOPT; + rq2.protocol = protocol; + rq2.adr = *adr; + rq2.ch = ch; + err = bp->create(&rq2); + if (err) + return err; + ch->recv = rq2.ch->send; + ch->peer = rq2.ch; + rq2.ch->st = dev->D.st; + rq.protocol = rq2.protocol; + rq.adr = *adr; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + if (err) { + rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL); + return err; + } + rq2.ch->recv = rq.ch->send; + rq2.ch->peer = rq.ch; + rq.ch->recv = rq2.ch->send; + rq.ch->peer = rq2.ch; + rq.ch->st = dev->D.st; + } + ch->protocol = protocol; + ch->nr = rq.ch->nr; + return 0; +} + +int +create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch, + u_int protocol, struct sockaddr_mISDN *adr) +{ + struct channel_req rq; + int err; + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, dev->name, protocol, + adr->dev, adr->channel, adr->sapi, + adr->tei); + rq.protocol = ISDN_P_TE_S0; + if (dev->Dprotocols & (1 << ISDN_P_TE_E1)) + rq.protocol = ISDN_P_TE_E1; + switch (protocol) { + case ISDN_P_LAPD_NT: + rq.protocol = ISDN_P_NT_S0; + if (dev->Dprotocols & (1 << ISDN_P_NT_E1)) + rq.protocol = ISDN_P_NT_E1; + case ISDN_P_LAPD_TE: +#ifdef PROTOCOL_CHECK + /* this should be enhanced */ + if (!list_empty(&dev->D.st->layer2) + && dev->D.protocol != protocol) + return -EBUSY; + if (!hlist_empty(&dev->D.st->l1sock.head) + && dev->D.protocol != protocol) + return -EBUSY; +#endif + ch->recv = mISDN_queue_message; + ch->peer = &dev->D.st->own; + ch->st = dev->D.st; + rq.adr.channel = 0; + err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); + printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); + if (err) + break; + rq.protocol = protocol; + rq.adr = *adr; + rq.ch = ch; + err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq); + printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err); + if (!err) { + if ((protocol == ISDN_P_LAPD_NT) && !rq.ch) + break; + add_layer2(rq.ch, dev->D.st); + rq.ch->recv = mISDN_queue_message; + rq.ch->peer = &dev->D.st->own; + rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */ + } + break; + default: + err = -EPROTONOSUPPORT; + } + return err; +} + +void +delete_channel(struct mISDNchannel *ch) +{ + struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch); + struct mISDNchannel *pch; + + if (!ch->st) { + printk(KERN_WARNING "%s: no stack\n", __func__); + return; + } + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__, + ch->st->dev->name, ch->protocol); + if (ch->protocol >= ISDN_P_B_START) { + if (ch->peer) { + ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL); + ch->peer = NULL; + } + return; + } + switch (ch->protocol) { + case ISDN_P_NT_S0: + case ISDN_P_TE_S0: + case ISDN_P_NT_E1: + case ISDN_P_TE_E1: + write_lock_bh(&ch->st->l1sock.lock); + sk_del_node_init(&msk->sk); + write_unlock_bh(&ch->st->l1sock.lock); + ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL); + break; + case ISDN_P_LAPD_TE: + pch = get_channel4id(ch->st, ch->nr); + if (pch) { + mutex_lock(&ch->st->lmutex); + list_del(&pch->list); + mutex_unlock(&ch->st->lmutex); + pch->ctrl(pch, CLOSE_CHANNEL, NULL); + pch = ch->st->dev->teimgr; + pch->ctrl(pch, CLOSE_CHANNEL, NULL); + } else + printk(KERN_WARNING "%s: no l2 channel\n", + __func__); + break; + case ISDN_P_LAPD_NT: + pch = ch->st->dev->teimgr; + if (pch) { + pch->ctrl(pch, CLOSE_CHANNEL, NULL); + } else + printk(KERN_WARNING "%s: no l2 channel\n", + __func__); + break; + default: + break; + } + return; +} + +void +delete_stack(struct mISDNdevice *dev) +{ + struct mISDNstack *st = dev->D.st; + DECLARE_COMPLETION_ONSTACK(done); + + if (*debug & DEBUG_CORE_FUNC) + printk(KERN_DEBUG "%s: st(%s)\n", __func__, + st->dev->name); + if (dev->teimgr) + delete_teimanager(dev->teimgr); + if (st->thread) { + if (st->notify) { + printk(KERN_WARNING "%s: notifier in use\n", + __func__); + complete(st->notify); + } + st->notify = &done; + test_and_set_bit(mISDN_STACK_ABORT, &st->status); + test_and_set_bit(mISDN_STACK_WAKEUP, &st->status); + wake_up_interruptible(&st->workq); + wait_for_completion(&done); + } + if (!list_empty(&st->layer2)) + printk(KERN_WARNING "%s: layer2 list not empty\n", + __func__); + if (!hlist_empty(&st->l1sock.head)) + printk(KERN_WARNING "%s: layer1 list not empty\n", + __func__); + kfree(st); +} + +void +mISDN_initstack(u_int *dp) +{ + debug = dp; +} diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c new file mode 100644 index 000000000000..56a76a0ffddd --- /dev/null +++ b/drivers/isdn/mISDN/tei.c @@ -0,0 +1,1340 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include "layer2.h" +#include +#include "core.h" + +#define ID_REQUEST 1 +#define ID_ASSIGNED 2 +#define ID_DENIED 3 +#define ID_CHK_REQ 4 +#define ID_CHK_RES 5 +#define ID_REMOVE 6 +#define ID_VERIFY 7 + +#define TEI_ENTITY_ID 0xf + +#define MGR_PH_ACTIVE 16 +#define MGR_PH_NOTREADY 17 + +#define DATIMER_VAL 10000 + +static u_int *debug; + +static struct Fsm deactfsm = {NULL, 0, 0, NULL, NULL}; +static struct Fsm teifsmu = {NULL, 0, 0, NULL, NULL}; +static struct Fsm teifsmn = {NULL, 0, 0, NULL, NULL}; + +enum { + ST_L1_DEACT, + ST_L1_DEACT_PENDING, + ST_L1_ACTIV, +}; +#define DEACT_STATE_COUNT (ST_L1_ACTIV+1) + +static char *strDeactState[] = +{ + "ST_L1_DEACT", + "ST_L1_DEACT_PENDING", + "ST_L1_ACTIV", +}; + +enum { + EV_ACTIVATE, + EV_ACTIVATE_IND, + EV_DEACTIVATE, + EV_DEACTIVATE_IND, + EV_UI, + EV_DATIMER, +}; + +#define DEACT_EVENT_COUNT (EV_DATIMER+1) + +static char *strDeactEvent[] = +{ + "EV_ACTIVATE", + "EV_ACTIVATE_IND", + "EV_DEACTIVATE", + "EV_DEACTIVATE_IND", + "EV_UI", + "EV_DATIMER", +}; + +static void +da_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct manager *mgr = fi->userdata; + va_list va; + + if (!(*debug & DEBUG_L2_TEIFSM)) + return; + va_start(va, fmt); + printk(KERN_DEBUG "mgr(%d): ", mgr->ch.st->dev->id); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + +static void +da_activate(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + + if (fi->state == ST_L1_DEACT_PENDING) + mISDN_FsmDelTimer(&mgr->datimer, 1); + mISDN_FsmChangeState(fi, ST_L1_ACTIV); +} + +static void +da_deactivate_ind(struct FsmInst *fi, int event, void *arg) +{ + mISDN_FsmChangeState(fi, ST_L1_DEACT); +} + +static void +da_deactivate(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + struct layer2 *l2; + u_long flags; + + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->l2m.state > ST_L2_4) { + /* have still activ TEI */ + read_unlock_irqrestore(&mgr->lock, flags); + return; + } + } + read_unlock_irqrestore(&mgr->lock, flags); + /* All TEI are inactiv */ + mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 1); + mISDN_FsmChangeState(fi, ST_L1_DEACT_PENDING); +} + +static void +da_ui(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + + /* restart da timer */ + mISDN_FsmDelTimer(&mgr->datimer, 2); + mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 2); + +} + +static void +da_timer(struct FsmInst *fi, int event, void *arg) +{ + struct manager *mgr = fi->userdata; + struct layer2 *l2; + u_long flags; + + /* check again */ + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->l2m.state > ST_L2_4) { + /* have still activ TEI */ + read_unlock_irqrestore(&mgr->lock, flags); + mISDN_FsmChangeState(fi, ST_L1_ACTIV); + return; + } + } + read_unlock_irqrestore(&mgr->lock, flags); + /* All TEI are inactiv */ + mISDN_FsmChangeState(fi, ST_L1_DEACT); + _queue_data(&mgr->ch, PH_DEACTIVATE_REQ, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); +} + +static struct FsmNode DeactFnList[] = +{ + {ST_L1_DEACT, EV_ACTIVATE_IND, da_activate}, + {ST_L1_ACTIV, EV_DEACTIVATE_IND, da_deactivate_ind}, + {ST_L1_ACTIV, EV_DEACTIVATE, da_deactivate}, + {ST_L1_DEACT_PENDING, EV_ACTIVATE, da_activate}, + {ST_L1_DEACT_PENDING, EV_UI, da_ui}, + {ST_L1_DEACT_PENDING, EV_DATIMER, da_timer}, +}; + +enum { + ST_TEI_NOP, + ST_TEI_IDREQ, + ST_TEI_IDVERIFY, +}; + +#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1) + +static char *strTeiState[] = +{ + "ST_TEI_NOP", + "ST_TEI_IDREQ", + "ST_TEI_IDVERIFY", +}; + +enum { + EV_IDREQ, + EV_ASSIGN, + EV_ASSIGN_REQ, + EV_DENIED, + EV_CHKREQ, + EV_CHKRESP, + EV_REMOVE, + EV_VERIFY, + EV_TIMER, +}; + +#define TEI_EVENT_COUNT (EV_TIMER+1) + +static char *strTeiEvent[] = +{ + "EV_IDREQ", + "EV_ASSIGN", + "EV_ASSIGN_REQ", + "EV_DENIED", + "EV_CHKREQ", + "EV_CHKRESP", + "EV_REMOVE", + "EV_VERIFY", + "EV_TIMER", +}; + +static void +tei_debug(struct FsmInst *fi, char *fmt, ...) +{ + struct teimgr *tm = fi->userdata; + va_list va; + + if (!(*debug & DEBUG_L2_TEIFSM)) + return; + va_start(va, fmt); + printk(KERN_DEBUG "tei(%d): ", tm->l2->tei); + vprintk(fmt, va); + printk("\n"); + va_end(va); +} + + + +static int +get_free_id(struct manager *mgr) +{ + u64 ids = 0; + int i; + struct layer2 *l2; + + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->ch.nr > 63) { + printk(KERN_WARNING + "%s: more as 63 layer2 for one device\n", + __func__); + return -EBUSY; + } + test_and_set_bit(l2->ch.nr, (u_long *)&ids); + } + for (i = 1; i < 64; i++) + if (!test_bit(i, (u_long *)&ids)) + return i; + printk(KERN_WARNING "%s: more as 63 layer2 for one device\n", + __func__); + return -EBUSY; +} + +static int +get_free_tei(struct manager *mgr) +{ + u64 ids = 0; + int i; + struct layer2 *l2; + + list_for_each_entry(l2, &mgr->layer2, list) { + if (l2->ch.nr == 0) + continue; + if ((l2->ch.addr & 0xff) != 0) + continue; + i = l2->ch.addr >> 8; + if (i < 64) + continue; + i -= 64; + + test_and_set_bit(i, (u_long *)&ids); + } + for (i = 0; i < 64; i++) + if (!test_bit(i, (u_long *)&ids)) + return i + 64; + printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n", + __func__); + return -1; +} + +static void +teiup_create(struct manager *mgr, u_int prim, int len, void *arg) +{ + struct sk_buff *skb; + struct mISDNhead *hh; + int err; + + skb = mI_alloc_skb(len, GFP_ATOMIC); + if (!skb) + return; + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = (mgr->ch.nr << 16) | mgr->ch.addr; + if (len) + memcpy(skb_put(skb, len), arg, len); + err = mgr->up->send(mgr->up, skb); + if (err) { + printk(KERN_WARNING "%s: err=%d\n", __func__, err); + dev_kfree_skb(skb); + } +} + +static u_int +new_id(struct manager *mgr) +{ + u_int id; + + id = mgr->nextid++; + if (id == 0x7fff) + mgr->nextid = 1; + id <<= 16; + id |= GROUP_TEI << 8; + id |= TEI_SAPI; + return id; +} + +static void +do_send(struct manager *mgr) +{ + if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) + return; + + if (!test_and_set_bit(MGR_PH_NOTREADY, &mgr->options)) { + struct sk_buff *skb = skb_dequeue(&mgr->sendq); + + if (!skb) { + test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); + return; + } + mgr->lastid = mISDN_HEAD_ID(skb); + mISDN_FsmEvent(&mgr->deact, EV_UI, NULL); + if (mgr->ch.recv(mgr->ch.peer, skb)) { + dev_kfree_skb(skb); + test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); + mgr->lastid = MISDN_ID_NONE; + } + } +} + +static void +do_ack(struct manager *mgr, u_int id) +{ + if (test_bit(MGR_PH_NOTREADY, &mgr->options)) { + if (id == mgr->lastid) { + if (test_bit(MGR_PH_ACTIVE, &mgr->options)) { + struct sk_buff *skb; + + skb = skb_dequeue(&mgr->sendq); + if (skb) { + mgr->lastid = mISDN_HEAD_ID(skb); + if (!mgr->ch.recv(mgr->ch.peer, skb)) + return; + dev_kfree_skb(skb); + } + } + mgr->lastid = MISDN_ID_NONE; + test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options); + } + } +} + +static void +mgr_send_down(struct manager *mgr, struct sk_buff *skb) +{ + skb_queue_tail(&mgr->sendq, skb); + if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) { + _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + } else { + do_send(mgr); + } +} + +static int +dl_unit_data(struct manager *mgr, struct sk_buff *skb) +{ + if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) /* only net send UI */ + return -EINVAL; + if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) + _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + skb_push(skb, 3); + skb->data[0] = 0x02; /* SAPI 0 C/R = 1 */ + skb->data[1] = 0xff; /* TEI 127 */ + skb->data[2] = UI; /* UI frame */ + mISDN_HEAD_PRIM(skb) = PH_DATA_REQ; + mISDN_HEAD_ID(skb) = new_id(mgr); + skb_queue_tail(&mgr->sendq, skb); + do_send(mgr); + return 0; +} + +unsigned int +random_ri(void) +{ + u16 x; + + get_random_bytes(&x, sizeof(x)); + return x; +} + +static struct layer2 * +findtei(struct manager *mgr, int tei) +{ + struct layer2 *l2; + u_long flags; + + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if ((l2->sapi == 0) && (l2->tei > 0) && + (l2->tei != GROUP_TEI) && (l2->tei == tei)) + goto done; + } + l2 = NULL; +done: + read_unlock_irqrestore(&mgr->lock, flags); + return l2; +} + +static void +put_tei_msg(struct manager *mgr, u_char m_id, unsigned int ri, u_char tei) +{ + struct sk_buff *skb; + u_char bp[8]; + + bp[0] = (TEI_SAPI << 2); + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) + bp[0] |= 2; /* CR:=1 for net command */ + bp[1] = (GROUP_TEI << 1) | 0x1; + bp[2] = UI; + bp[3] = TEI_ENTITY_ID; + bp[4] = ri >> 8; + bp[5] = ri & 0xff; + bp[6] = m_id; + bp[7] = (tei << 1) | 1; + skb = _alloc_mISDN_skb(PH_DATA_REQ, new_id(mgr), + 8, bp, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING "%s: no skb for tei msg\n", __func__); + return; + } + mgr_send_down(mgr, skb); +} + +static void +tei_id_request(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (tm->l2->tei != GROUP_TEI) { + tm->tei_m.printdebug(&tm->tei_m, + "assign request for allready assigned tei %d", + tm->l2->tei); + return; + } + tm->ri = random_ri(); + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(&tm->tei_m, + "assign request ri %d", tm->ri); + put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI); + mISDN_FsmChangeState(fi, ST_TEI_IDREQ); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 1); + tm->nval = 3; +} + +static void +tei_id_assign(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + struct layer2 *l2; + u_char *dp = arg; + int ri, tei; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity assign ri %d tei %d", + ri, tei); + l2 = findtei(tm->mgr, tei); + if (l2) { /* same tei is in use */ + if (ri != l2->tm->ri) { + tm->tei_m.printdebug(fi, + "possible duplicate assignment tei %d", tei); + tei_l2(l2, MDL_ERROR_RSP, 0); + } + } else if (ri == tm->ri) { + mISDN_FsmDelTimer(&tm->timer, 1); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + tei_l2(tm->l2, MDL_ASSIGN_REQ, tei); + } +} + +static void +tei_id_test_dup(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + struct layer2 *l2; + u_char *dp = arg; + int tei, ri; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d", + ri, tei); + l2 = findtei(tm->mgr, tei); + if (l2) { /* same tei is in use */ + if (ri != l2->tm->ri) { /* and it wasn't our request */ + tm->tei_m.printdebug(fi, + "possible duplicate assignment tei %d", tei); + mISDN_FsmEvent(&l2->tm->tei_m, EV_VERIFY, NULL); + } + } +} + +static void +tei_id_denied(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int ri, tei; + + ri = ((unsigned int) *dp++ << 8); + ri += *dp++; + dp++; + tei = *dp >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity denied ri %d tei %d", + ri, tei); +} + +static void +tei_id_chk_req(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = *(dp+3) >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity check req tei %d", tei); + if ((tm->l2->tei != GROUP_TEI) && ((tei == GROUP_TEI) || + (tei == tm->l2->tei))) { + mISDN_FsmDelTimer(&tm->timer, 4); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); + put_tei_msg(tm->mgr, ID_CHK_RES, random_ri(), tm->l2->tei); + } +} + +static void +tei_id_remove(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = *(dp+3) >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity remove tei %d", tei); + if ((tm->l2->tei != GROUP_TEI) && + ((tei == GROUP_TEI) || (tei == tm->l2->tei))) { + mISDN_FsmDelTimer(&tm->timer, 5); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP); + tei_l2(tm->l2, MDL_REMOVE_REQ, 0); + } +} + +static void +tei_id_verify(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "id verify request for tei %d", + tm->l2->tei); + put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2); + tm->nval = 2; +} + +static void +tei_id_req_tout(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (--tm->nval) { + tm->ri = random_ri(); + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "assign req(%d) ri %d", + 4 - tm->nval, tm->ri); + put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 3); + } else { + tm->tei_m.printdebug(fi, "assign req failed"); + tei_l2(tm->l2, MDL_ERROR_RSP, 0); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } +} + +static void +tei_id_ver_tout(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (--tm->nval) { + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, + "id verify req(%d) for tei %d", + 3 - tm->nval, tm->l2->tei); + put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4); + } else { + tm->tei_m.printdebug(fi, "verify req for tei %d failed", + tm->l2->tei); + tei_l2(tm->l2, MDL_REMOVE_REQ, 0); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } +} + +static struct FsmNode TeiFnListUser[] = +{ + {ST_TEI_NOP, EV_IDREQ, tei_id_request}, + {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup}, + {ST_TEI_NOP, EV_VERIFY, tei_id_verify}, + {ST_TEI_NOP, EV_REMOVE, tei_id_remove}, + {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req}, + {ST_TEI_IDREQ, EV_TIMER, tei_id_req_tout}, + {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign}, + {ST_TEI_IDREQ, EV_DENIED, tei_id_denied}, + {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout}, + {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove}, + {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req}, +}; + +static void +tei_l2remove(struct layer2 *l2) +{ + put_tei_msg(l2->tm->mgr, ID_REMOVE, 0, l2->tei); + tei_l2(l2, MDL_REMOVE_REQ, 0); + list_del(&l2->ch.list); + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); +} + +static void +tei_assign_req(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + + if (tm->l2->tei == GROUP_TEI) { + tm->tei_m.printdebug(&tm->tei_m, + "net tei assign request without tei"); + return; + } + tm->ri = ((unsigned int) *dp++ << 8); + tm->ri += *dp++; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(&tm->tei_m, + "net assign request ri %d teim %d", tm->ri, *dp); + put_tei_msg(tm->mgr, ID_ASSIGNED, tm->ri, tm->l2->tei); + mISDN_FsmChangeState(fi, ST_TEI_NOP); +} + +static void +tei_id_chk_req_net(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "id check request for tei %d", + tm->l2->tei); + tm->rcnt = 0; + put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei); + mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2); + tm->nval = 2; +} + +static void +tei_id_chk_resp(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = dp[3] >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity check resp tei %d", tei); + if (tei == tm->l2->tei) + tm->rcnt++; +} + +static void +tei_id_verify_net(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + u_char *dp = arg; + int tei; + + tei = dp[3] >> 1; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, "identity verify req tei %d/%d", + tei, tm->l2->tei); + if (tei == tm->l2->tei) + tei_id_chk_req_net(fi, event, arg); +} + +static void +tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg) +{ + struct teimgr *tm = fi->userdata; + + if (tm->rcnt == 1) { + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, + "check req for tei %d sucessful\n", tm->l2->tei); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + } else if (tm->rcnt > 1) { + /* duplicate assignment; remove */ + tei_l2remove(tm->l2); + } else if (--tm->nval) { + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(fi, + "id check req(%d) for tei %d", + 3 - tm->nval, tm->l2->tei); + put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei); + mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4); + } else { + tm->tei_m.printdebug(fi, "check req for tei %d failed", + tm->l2->tei); + mISDN_FsmChangeState(fi, ST_TEI_NOP); + tei_l2remove(tm->l2); + } +} + +static struct FsmNode TeiFnListNet[] = +{ + {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req}, + {ST_TEI_NOP, EV_VERIFY, tei_id_verify_net}, + {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req_net}, + {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout_net}, + {ST_TEI_IDVERIFY, EV_CHKRESP, tei_id_chk_resp}, +}; + +static void +tei_ph_data_ind(struct teimgr *tm, u_int mt, u_char *dp, int len) +{ + if (test_bit(FLG_FIXED_TEI, &tm->l2->flag)) + return; + if (*debug & DEBUG_L2_TEI) + tm->tei_m.printdebug(&tm->tei_m, "tei handler mt %x", mt); + if (mt == ID_ASSIGNED) + mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN, dp); + else if (mt == ID_DENIED) + mISDN_FsmEvent(&tm->tei_m, EV_DENIED, dp); + else if (mt == ID_CHK_REQ) + mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, dp); + else if (mt == ID_REMOVE) + mISDN_FsmEvent(&tm->tei_m, EV_REMOVE, dp); + else if (mt == ID_VERIFY) + mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, dp); + else if (mt == ID_CHK_RES) + mISDN_FsmEvent(&tm->tei_m, EV_CHKRESP, dp); +} + +static struct layer2 * +create_new_tei(struct manager *mgr, int tei) +{ + u_long opt = 0; + u_long flags; + int id; + struct layer2 *l2; + + if (!mgr->up) + return NULL; + if (tei < 64) + test_and_set_bit(OPTION_L2_FIXEDTEI, &opt); + if (mgr->ch.st->dev->Dprotocols + & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1))) + test_and_set_bit(OPTION_L2_PMX, &opt); + l2 = create_l2(mgr->up, ISDN_P_LAPD_NT, (u_int)opt, (u_long)tei); + if (!l2) { + printk(KERN_WARNING "%s:no memory for layer2\n", __func__); + return NULL; + } + l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL); + if (!l2->tm) { + kfree(l2); + printk(KERN_WARNING "%s:no memory for teimgr\n", __func__); + return NULL; + } + l2->tm->mgr = mgr; + l2->tm->l2 = l2; + l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM; + l2->tm->tei_m.userdata = l2->tm; + l2->tm->tei_m.printdebug = tei_debug; + l2->tm->tei_m.fsm = &teifsmn; + l2->tm->tei_m.state = ST_TEI_NOP; + l2->tm->tval = 2000; /* T202 2 sec */ + mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer); + write_lock_irqsave(&mgr->lock, flags); + id = get_free_id(mgr); + list_add_tail(&l2->list, &mgr->layer2); + write_unlock_irqrestore(&mgr->lock, flags); + if (id < 0) { + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + printk(KERN_WARNING "%s:no free id\n", __func__); + return NULL; + } else { + l2->ch.nr = id; + __add_layer2(&l2->ch, mgr->ch.st); + l2->ch.recv = mgr->ch.recv; + l2->ch.peer = mgr->ch.peer; + l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL); + } + return l2; +} + +static void +new_tei_req(struct manager *mgr, u_char *dp) +{ + int tei, ri; + struct layer2 *l2; + + ri = dp[0] << 8; + ri += dp[1]; + if (!mgr->up) + goto denied; + tei = get_free_tei(mgr); + if (tei < 0) { + printk(KERN_WARNING "%s:No free tei\n", __func__); + goto denied; + } + l2 = create_new_tei(mgr, tei); + if (!l2) + goto denied; + else + mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp); + return; +denied: + put_tei_msg(mgr, ID_DENIED, ri, GROUP_TEI); +} + +static int +ph_data_ind(struct manager *mgr, struct sk_buff *skb) +{ + int ret = -EINVAL; + struct layer2 *l2; + u_long flags; + u_char mt; + + if (skb->len < 8) { + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: short mgr frame %d/8\n", + __func__, skb->len); + goto done; + } + if (*debug & DEBUG_L2_TEI) + + if ((skb->data[0] >> 2) != TEI_SAPI) /* not for us */ + goto done; + if (skb->data[0] & 1) /* EA0 formal error */ + goto done; + if (!(skb->data[1] & 1)) /* EA1 formal error */ + goto done; + if ((skb->data[1] >> 1) != GROUP_TEI) /* not for us */ + goto done; + if ((skb->data[2] & 0xef) != UI) /* not UI */ + goto done; + if (skb->data[3] != TEI_ENTITY_ID) /* not tei entity */ + goto done; + mt = skb->data[6]; + switch (mt) { + case ID_REQUEST: + case ID_CHK_RES: + case ID_VERIFY: + if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) + goto done; + break; + case ID_ASSIGNED: + case ID_DENIED: + case ID_CHK_REQ: + case ID_REMOVE: + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) + goto done; + break; + default: + goto done; + } + ret = 0; + if (mt == ID_REQUEST) { + new_tei_req(mgr, &skb->data[4]); + goto done; + } + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + tei_ph_data_ind(l2->tm, mt, &skb->data[4], skb->len - 4); + } + read_unlock_irqrestore(&mgr->lock, flags); +done: + return ret; +} + +int +l2_tei(struct layer2 *l2, u_int cmd, u_long arg) +{ + struct teimgr *tm = l2->tm; + + if (test_bit(FLG_FIXED_TEI, &l2->flag)) + return 0; + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd); + switch (cmd) { + case MDL_ASSIGN_IND: + mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL); + break; + case MDL_ERROR_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, &l2->tei); + if (test_bit(MGR_OPT_USER, &tm->mgr->options)) + mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL); + break; + case MDL_STATUS_UP_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->mgr->deact, EV_ACTIVATE, NULL); + break; + case MDL_STATUS_DOWN_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->mgr->deact, EV_DEACTIVATE, NULL); + break; + case MDL_STATUS_UI_IND: + if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options)) + mISDN_FsmEvent(&tm->mgr->deact, EV_UI, NULL); + break; + } + return 0; +} + +void +release_tei(struct layer2 *l2) +{ + struct teimgr *tm = l2->tm; + u_long flags; + + mISDN_FsmDelTimer(&tm->timer, 1); + write_lock_irqsave(&tm->mgr->lock, flags); + list_del(&l2->list); + write_unlock_irqrestore(&tm->mgr->lock, flags); + l2->tm = NULL; + kfree(tm); +} + +static int +create_teimgr(struct manager *mgr, struct channel_req *crq) +{ + struct layer2 *l2; + u_long opt = 0; + u_long flags; + int id; + + if (*debug & DEBUG_L2_TEI) + printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", + __func__, mgr->ch.st->dev->name, crq->protocol, + crq->adr.dev, crq->adr.channel, crq->adr.sapi, + crq->adr.tei); + if (crq->adr.sapi != 0) /* not supported yet */ + return -EINVAL; + if (crq->adr.tei > GROUP_TEI) + return -EINVAL; + if (crq->adr.tei < 64) + test_and_set_bit(OPTION_L2_FIXEDTEI, &opt); + if (crq->adr.tei == 0) + test_and_set_bit(OPTION_L2_PTP, &opt); + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) { + if (crq->protocol == ISDN_P_LAPD_TE) + return -EPROTONOSUPPORT; + if ((crq->adr.tei != 0) && (crq->adr.tei != 127)) + return -EINVAL; + if (mgr->up) { + printk(KERN_WARNING + "%s: only one network manager is allowed\n", + __func__); + return -EBUSY; + } + } else if (test_bit(MGR_OPT_USER, &mgr->options)) { + if (crq->protocol == ISDN_P_LAPD_NT) + return -EPROTONOSUPPORT; + if ((crq->adr.tei >= 64) && (crq->adr.tei < GROUP_TEI)) + return -EINVAL; /* dyn tei */ + } else { + if (crq->protocol == ISDN_P_LAPD_NT) + test_and_set_bit(MGR_OPT_NETWORK, &mgr->options); + if (crq->protocol == ISDN_P_LAPD_TE) + test_and_set_bit(MGR_OPT_USER, &mgr->options); + } + if (mgr->ch.st->dev->Dprotocols + & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1))) + test_and_set_bit(OPTION_L2_PMX, &opt); + if ((crq->protocol == ISDN_P_LAPD_NT) && (crq->adr.tei == 127)) { + mgr->up = crq->ch; + id = DL_INFO_L2_CONNECT; + teiup_create(mgr, DL_INFORMATION_IND, sizeof(id), &id); + crq->ch = NULL; + if (!list_empty(&mgr->layer2)) { + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + l2->up = mgr->up; + l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL); + } + read_unlock_irqrestore(&mgr->lock, flags); + } + return 0; + } + l2 = create_l2(crq->ch, crq->protocol, (u_int)opt, + (u_long)crq->adr.tei); + if (!l2) + return -ENOMEM; + l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL); + if (!l2->tm) { + kfree(l2); + printk(KERN_ERR "kmalloc teimgr failed\n"); + return -ENOMEM; + } + l2->tm->mgr = mgr; + l2->tm->l2 = l2; + l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM; + l2->tm->tei_m.userdata = l2->tm; + l2->tm->tei_m.printdebug = tei_debug; + if (crq->protocol == ISDN_P_LAPD_TE) { + l2->tm->tei_m.fsm = &teifsmu; + l2->tm->tei_m.state = ST_TEI_NOP; + l2->tm->tval = 1000; /* T201 1 sec */ + } else { + l2->tm->tei_m.fsm = &teifsmn; + l2->tm->tei_m.state = ST_TEI_NOP; + l2->tm->tval = 2000; /* T202 2 sec */ + } + mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer); + write_lock_irqsave(&mgr->lock, flags); + id = get_free_id(mgr); + list_add_tail(&l2->list, &mgr->layer2); + write_unlock_irqrestore(&mgr->lock, flags); + if (id < 0) { + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + } else { + l2->ch.nr = id; + l2->up->nr = id; + crq->ch = &l2->ch; + id = 0; + } + return id; +} + +static int +mgr_send(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct manager *mgr; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + + mgr = container_of(ch, struct manager, ch); + if (*debug & DEBUG_L2_RECV) + printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", + __func__, hh->prim, hh->id); + switch (hh->prim) { + case PH_DATA_IND: + mISDN_FsmEvent(&mgr->deact, EV_UI, NULL); + ret = ph_data_ind(mgr, skb); + break; + case PH_DATA_CNF: + do_ack(mgr, hh->id); + ret = 0; + break; + case PH_ACTIVATE_IND: + test_and_set_bit(MGR_PH_ACTIVE, &mgr->options); + mISDN_FsmEvent(&mgr->deact, EV_ACTIVATE_IND, NULL); + do_send(mgr); + ret = 0; + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(MGR_PH_ACTIVE, &mgr->options); + mISDN_FsmEvent(&mgr->deact, EV_DEACTIVATE_IND, NULL); + ret = 0; + break; + case DL_UNITDATA_REQ: + return dl_unit_data(mgr, skb); + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +free_teimanager(struct manager *mgr) +{ + struct layer2 *l2, *nl2; + + if (test_bit(MGR_OPT_NETWORK, &mgr->options)) { + /* not locked lock is taken in release tei */ + mgr->up = NULL; + if (test_bit(OPTION_L2_CLEANUP, &mgr->options)) { + list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { + put_tei_msg(mgr, ID_REMOVE, 0, l2->tei); + mutex_lock(&mgr->ch.st->lmutex); + list_del(&l2->ch.list); + mutex_unlock(&mgr->ch.st->lmutex); + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + } + test_and_clear_bit(MGR_OPT_NETWORK, &mgr->options); + } else { + list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { + l2->up = NULL; + } + } + } + if (test_bit(MGR_OPT_USER, &mgr->options)) { + if (list_empty(&mgr->layer2)) + test_and_clear_bit(MGR_OPT_USER, &mgr->options); + } + mgr->ch.st->dev->D.ctrl(&mgr->ch.st->dev->D, CLOSE_CHANNEL, NULL); + return 0; +} + +static int +ctrl_teimanager(struct manager *mgr, void *arg) +{ + /* currently we only have one option */ + int clean = *((int *)arg); + + if (clean) + test_and_set_bit(OPTION_L2_CLEANUP, &mgr->options); + else + test_and_clear_bit(OPTION_L2_CLEANUP, &mgr->options); + return 0; +} + +/* This function does create a L2 for fixed TEI in NT Mode */ +static int +check_data(struct manager *mgr, struct sk_buff *skb) +{ + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret, tei; + struct layer2 *l2; + + if (*debug & DEBUG_L2_CTRL) + printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", + __func__, hh->prim, hh->id); + if (test_bit(MGR_OPT_USER, &mgr->options)) + return -ENOTCONN; + if (hh->prim != PH_DATA_IND) + return -ENOTCONN; + if (skb->len != 3) + return -ENOTCONN; + if (skb->data[0] != 0) + /* only SAPI 0 command */ + return -ENOTCONN; + if (!(skb->data[1] & 1)) /* invalid EA1 */ + return -EINVAL; + tei = skb->data[1] >> 0; + if (tei > 63) /* not a fixed tei */ + return -ENOTCONN; + if ((skb->data[2] & ~0x10) != SABME) + return -ENOTCONN; + /* We got a SABME for a fixed TEI */ + l2 = create_new_tei(mgr, tei); + if (!l2) + return -ENOMEM; + ret = l2->ch.send(&l2->ch, skb); + return ret; +} + +void +delete_teimanager(struct mISDNchannel *ch) +{ + struct manager *mgr; + struct layer2 *l2, *nl2; + + mgr = container_of(ch, struct manager, ch); + /* not locked lock is taken in release tei */ + list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) { + mutex_lock(&mgr->ch.st->lmutex); + list_del(&l2->ch.list); + mutex_unlock(&mgr->ch.st->lmutex); + l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL); + } + list_del(&mgr->ch.list); + list_del(&mgr->bcast.list); + skb_queue_purge(&mgr->sendq); + kfree(mgr); +} + +static int +mgr_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct manager *mgr; + int ret = -EINVAL; + + mgr = container_of(ch, struct manager, ch); + if (*debug & DEBUG_L2_CTRL) + printk(KERN_DEBUG "%s(%x, %p)\n", __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + ret = create_teimgr(mgr, arg); + break; + case CLOSE_CHANNEL: + ret = free_teimanager(mgr); + break; + case CONTROL_CHANNEL: + ret = ctrl_teimanager(mgr, arg); + break; + case CHECK_DATA: + ret = check_data(mgr, arg); + break; + } + return ret; +} + +static int +mgr_bcast(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct manager *mgr = container_of(ch, struct manager, bcast); + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct sk_buff *cskb = NULL; + struct layer2 *l2; + u_long flags; + int ret; + + read_lock_irqsave(&mgr->lock, flags); + list_for_each_entry(l2, &mgr->layer2, list) { + if ((hh->id & MISDN_ID_SAPI_MASK) == + (l2->ch.addr & MISDN_ID_SAPI_MASK)) { + if (list_is_last(&l2->list, &mgr->layer2)) { + cskb = skb; + skb = NULL; + } else { + if (!cskb) + cskb = skb_copy(skb, GFP_KERNEL); + } + if (cskb) { + ret = l2->ch.send(&l2->ch, cskb); + if (ret) { + if (*debug & DEBUG_SEND_ERR) + printk(KERN_DEBUG + "%s ch%d prim(%x) addr(%x)" + " err %d\n", + __func__, l2->ch.nr, + hh->prim, l2->ch.addr, ret); + } else + cskb = NULL; + } else { + printk(KERN_WARNING "%s ch%d addr %x no mem\n", + __func__, ch->nr, ch->addr); + goto out; + } + } + } +out: + read_unlock_irqrestore(&mgr->lock, flags); + if (cskb) + dev_kfree_skb(cskb); + if (skb) + dev_kfree_skb(skb); + return 0; +} + +static int +mgr_bcast_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + + return -EINVAL; +} + +int +create_teimanager(struct mISDNdevice *dev) +{ + struct manager *mgr; + + mgr = kzalloc(sizeof(struct manager), GFP_KERNEL); + if (!mgr) + return -ENOMEM; + INIT_LIST_HEAD(&mgr->layer2); + mgr->lock = __RW_LOCK_UNLOCKED(mgr->lock); + skb_queue_head_init(&mgr->sendq); + mgr->nextid = 1; + mgr->lastid = MISDN_ID_NONE; + mgr->ch.send = mgr_send; + mgr->ch.ctrl = mgr_ctrl; + mgr->ch.st = dev->D.st; + set_channel_address(&mgr->ch, TEI_SAPI, GROUP_TEI); + add_layer2(&mgr->ch, dev->D.st); + mgr->bcast.send = mgr_bcast; + mgr->bcast.ctrl = mgr_bcast_ctrl; + mgr->bcast.st = dev->D.st; + set_channel_address(&mgr->bcast, 0, GROUP_TEI); + add_layer2(&mgr->bcast, dev->D.st); + mgr->deact.debug = *debug & DEBUG_MANAGER; + mgr->deact.userdata = mgr; + mgr->deact.printdebug = da_debug; + mgr->deact.fsm = &deactfsm; + mgr->deact.state = ST_L1_DEACT; + mISDN_FsmInitTimer(&mgr->deact, &mgr->datimer); + dev->teimgr = &mgr->ch; + return 0; +} + +int TEIInit(u_int *deb) +{ + debug = deb; + teifsmu.state_count = TEI_STATE_COUNT; + teifsmu.event_count = TEI_EVENT_COUNT; + teifsmu.strEvent = strTeiEvent; + teifsmu.strState = strTeiState; + mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser)); + teifsmn.state_count = TEI_STATE_COUNT; + teifsmn.event_count = TEI_EVENT_COUNT; + teifsmn.strEvent = strTeiEvent; + teifsmn.strState = strTeiState; + mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet)); + deactfsm.state_count = DEACT_STATE_COUNT; + deactfsm.event_count = DEACT_EVENT_COUNT; + deactfsm.strEvent = strDeactEvent; + deactfsm.strState = strDeactState; + mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList)); + return 0; +} + +void TEIFree(void) +{ + mISDN_FsmFree(&teifsmu); + mISDN_FsmFree(&teifsmn); + mISDN_FsmFree(&deactfsm); +} diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c new file mode 100644 index 000000000000..b5fabc7019d8 --- /dev/null +++ b/drivers/isdn/mISDN/timerdev.c @@ -0,0 +1,301 @@ +/* + * + * general timer device for using in ISDN stacks + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +static int *debug; + + +struct mISDNtimerdev { + int next_id; + struct list_head pending; + struct list_head expired; + wait_queue_head_t wait; + u_int work; + spinlock_t lock; /* protect lists */ +}; + +struct mISDNtimer { + struct list_head list; + struct mISDNtimerdev *dev; + struct timer_list tl; + int id; +}; + +static int +mISDN_open(struct inode *ino, struct file *filep) +{ + struct mISDNtimerdev *dev; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); + dev = kmalloc(sizeof(struct mISDNtimerdev) , GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->next_id = 1; + INIT_LIST_HEAD(&dev->pending); + INIT_LIST_HEAD(&dev->expired); + spin_lock_init(&dev->lock); + dev->work = 0; + init_waitqueue_head(&dev->wait); + filep->private_data = dev; + __module_get(THIS_MODULE); + return 0; +} + +static int +mISDN_close(struct inode *ino, struct file *filep) +{ + struct mISDNtimerdev *dev = filep->private_data; + struct mISDNtimer *timer, *next; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep); + list_for_each_entry_safe(timer, next, &dev->pending, list) { + del_timer(&timer->tl); + kfree(timer); + } + list_for_each_entry_safe(timer, next, &dev->expired, list) { + kfree(timer); + } + kfree(dev); + module_put(THIS_MODULE); + return 0; +} + +static ssize_t +mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off) +{ + struct mISDNtimerdev *dev = filep->private_data; + struct mISDNtimer *timer; + u_long flags; + int ret = 0; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__, + filep, buf, (int)count, off); + if (*off != filep->f_pos) + return -ESPIPE; + + if (list_empty(&dev->expired) && (dev->work == 0)) { + if (filep->f_flags & O_NONBLOCK) + return -EAGAIN; + wait_event_interruptible(dev->wait, (dev->work || + !list_empty(&dev->expired))); + if (signal_pending(current)) + return -ERESTARTSYS; + } + if (count < sizeof(int)) + return -ENOSPC; + if (dev->work) + dev->work = 0; + if (!list_empty(&dev->expired)) { + spin_lock_irqsave(&dev->lock, flags); + timer = (struct mISDNtimer *)dev->expired.next; + list_del(&timer->list); + spin_unlock_irqrestore(&dev->lock, flags); + if (put_user(timer->id, (int *)buf)) + ret = -EFAULT; + else + ret = sizeof(int); + kfree(timer); + } + return ret; +} + +static loff_t +mISDN_llseek(struct file *filep, loff_t offset, int orig) +{ + return -ESPIPE; +} + +static ssize_t +mISDN_write(struct file *filep, const char *buf, size_t count, loff_t *off) +{ + return -EOPNOTSUPP; +} + +static unsigned int +mISDN_poll(struct file *filep, poll_table *wait) +{ + struct mISDNtimerdev *dev = filep->private_data; + unsigned int mask = POLLERR; + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait); + if (dev) { + poll_wait(filep, &dev->wait, wait); + mask = 0; + if (dev->work || !list_empty(&dev->expired)) + mask |= (POLLIN | POLLRDNORM); + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__, + dev->work, list_empty(&dev->expired)); + } + return mask; +} + +static void +dev_expire_timer(struct mISDNtimer *timer) +{ + u_long flags; + + spin_lock_irqsave(&timer->dev->lock, flags); + list_del(&timer->list); + list_add_tail(&timer->list, &timer->dev->expired); + spin_unlock_irqrestore(&timer->dev->lock, flags); + wake_up_interruptible(&timer->dev->wait); +} + +static int +misdn_add_timer(struct mISDNtimerdev *dev, int timeout) +{ + int id; + u_long flags; + struct mISDNtimer *timer; + + if (!timeout) { + dev->work = 1; + wake_up_interruptible(&dev->wait); + id = 0; + } else { + timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL); + if (!timer) + return -ENOMEM; + spin_lock_irqsave(&dev->lock, flags); + timer->id = dev->next_id++; + if (dev->next_id < 0) + dev->next_id = 1; + list_add_tail(&timer->list, &dev->pending); + spin_unlock_irqrestore(&dev->lock, flags); + timer->dev = dev; + timer->tl.data = (long)timer; + timer->tl.function = (void *) dev_expire_timer; + init_timer(&timer->tl); + timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000); + add_timer(&timer->tl); + id = timer->id; + } + return id; +} + +static int +misdn_del_timer(struct mISDNtimerdev *dev, int id) +{ + u_long flags; + struct mISDNtimer *timer; + int ret = 0; + + spin_lock_irqsave(&dev->lock, flags); + list_for_each_entry(timer, &dev->pending, list) { + if (timer->id == id) { + list_del_init(&timer->list); + del_timer(&timer->tl); + ret = timer->id; + kfree(timer); + goto unlock; + } + } +unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int +mISDN_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct mISDNtimerdev *dev = filep->private_data; + int id, tout, ret = 0; + + + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__, + filep, cmd, arg); + switch (cmd) { + case IMADDTIMER: + if (get_user(tout, (int __user *)arg)) { + ret = -EFAULT; + break; + } + id = misdn_add_timer(dev, tout); + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s add %d id %d\n", __func__, + tout, id); + if (id < 0) { + ret = id; + break; + } + if (put_user(id, (int __user *)arg)) + ret = -EFAULT; + break; + case IMDELTIMER: + if (get_user(id, (int __user *)arg)) { + ret = -EFAULT; + break; + } + if (*debug & DEBUG_TIMER) + printk(KERN_DEBUG "%s del id %d\n", __func__, id); + id = misdn_del_timer(dev, id); + if (put_user(id, (int __user *)arg)) + ret = -EFAULT; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static struct file_operations mISDN_fops = { + .llseek = mISDN_llseek, + .read = mISDN_read, + .write = mISDN_write, + .poll = mISDN_poll, + .ioctl = mISDN_ioctl, + .open = mISDN_open, + .release = mISDN_close, +}; + +static struct miscdevice mISDNtimer = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mISDNtimer", + .fops = &mISDN_fops, +}; + +int +mISDN_inittimer(int *deb) +{ + int err; + + debug = deb; + err = misc_register(&mISDNtimer); + if (err) + printk(KERN_WARNING "mISDN: Could not register timer device\n"); + return err; +} + +void mISDN_timer_cleanup(void) +{ + misc_deregister(&mISDNtimer); +} diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h new file mode 100644 index 000000000000..e794dfb87504 --- /dev/null +++ b/include/linux/mISDNhw.h @@ -0,0 +1,193 @@ +/* + * + * Author Karsten Keil + * + * Basic declarations for the mISDN HW channels + * + * Copyright 2008 by Karsten Keil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef MISDNHW_H +#define MISDNHW_H +#include +#include + +/* + * HW DEBUG 0xHHHHGGGG + * H - hardware driver specific bits + * G - for all drivers + */ + +#define DEBUG_HW 0x00000001 +#define DEBUG_HW_OPEN 0x00000002 +#define DEBUG_HW_DCHANNEL 0x00000100 +#define DEBUG_HW_DFIFO 0x00000200 +#define DEBUG_HW_BCHANNEL 0x00001000 +#define DEBUG_HW_BFIFO 0x00002000 + +#define MAX_DFRAME_LEN_L1 300 +#define MAX_MON_FRAME 32 +#define MAX_LOG_SPACE 2048 +#define MISDN_COPY_SIZE 32 + +/* channel->Flags bit field */ +#define FLG_TX_BUSY 0 /* tx_buf in use */ +#define FLG_TX_NEXT 1 /* next_skb in use */ +#define FLG_L1_BUSY 2 /* L1 is permanent busy */ +#define FLG_L2_ACTIVATED 3 /* activated from L2 */ +#define FLG_OPEN 5 /* channel is in use */ +#define FLG_ACTIVE 6 /* channel is activated */ +#define FLG_BUSY_TIMER 7 +/* channel type */ +#define FLG_DCHANNEL 8 /* channel is D-channel */ +#define FLG_BCHANNEL 9 /* channel is B-channel */ +#define FLG_ECHANNEL 10 /* channel is E-channel */ +#define FLG_TRANSPARENT 12 /* channel use transparent data */ +#define FLG_HDLC 13 /* channel use hdlc data */ +#define FLG_L2DATA 14 /* channel use L2 DATA primitivs */ +#define FLG_ORIGIN 15 /* channel is on origin site */ +/* channel specific stuff */ +/* arcofi specific */ +#define FLG_ARCOFI_TIMER 16 +#define FLG_ARCOFI_ERROR 17 +/* isar specific */ +#define FLG_INITIALIZED 16 +#define FLG_DLEETX 17 +#define FLG_LASTDLE 18 +#define FLG_FIRST 19 +#define FLG_LASTDATA 20 +#define FLG_NMD_DATA 21 +#define FLG_FTI_RUN 22 +#define FLG_LL_OK 23 +#define FLG_LL_CONN 24 +#define FLG_DTMFSEND 25 + +/* workq events */ +#define FLG_RECVQUEUE 30 +#define FLG_PHCHANGE 31 + +#define schedule_event(s, ev) do { \ + test_and_set_bit(ev, &((s)->Flags)); \ + schedule_work(&((s)->workq)); \ + } while (0) + +struct dchannel { + struct mISDNdevice dev; + u_long Flags; + struct work_struct workq; + void (*phfunc) (struct dchannel *); + u_int state; + void *l1; + /* HW access */ + u_char (*read_reg) (void *, u_char); + void (*write_reg) (void *, u_char, u_char); + void (*read_fifo) (void *, u_char *, int); + void (*write_fifo) (void *, u_char *, int); + void *hw; + int slot; /* multiport card channel slot */ + struct timer_list timer; + /* receive data */ + struct sk_buff *rx_skb; + int maxlen; + /* send data */ + struct sk_buff_head squeue; + struct sk_buff_head rqueue; + struct sk_buff *tx_skb; + int tx_idx; + int debug; + /* statistics */ + int err_crc; + int err_tx; + int err_rx; +}; + +typedef int (dchannel_l1callback)(struct dchannel *, u_int); +extern int create_l1(struct dchannel *, dchannel_l1callback *); + +/* private L1 commands */ +#define INFO0 0x8002 +#define INFO1 0x8102 +#define INFO2 0x8202 +#define INFO3_P8 0x8302 +#define INFO3_P10 0x8402 +#define INFO4_P8 0x8502 +#define INFO4_P10 0x8602 +#define LOSTFRAMING 0x8702 +#define ANYSIGNAL 0x8802 +#define HW_POWERDOWN 0x8902 +#define HW_RESET_REQ 0x8a02 +#define HW_POWERUP_REQ 0x8b02 +#define HW_DEACT_REQ 0x8c02 +#define HW_ACTIVATE_REQ 0x8e02 +#define HW_D_NOBLOCKED 0x8f02 +#define HW_RESET_IND 0x9002 +#define HW_POWERUP_IND 0x9102 +#define HW_DEACT_IND 0x9202 +#define HW_ACTIVATE_IND 0x9302 +#define HW_DEACT_CNF 0x9402 +#define HW_TESTLOOP 0x9502 +#define HW_TESTRX_RAW 0x9602 +#define HW_TESTRX_HDLC 0x9702 +#define HW_TESTRX_OFF 0x9802 + +struct layer1; +extern int l1_event(struct layer1 *, u_int); + + +struct bchannel { + struct mISDNchannel ch; + int nr; + u_long Flags; + struct work_struct workq; + u_int state; + /* HW access */ + u_char (*read_reg) (void *, u_char); + void (*write_reg) (void *, u_char, u_char); + void (*read_fifo) (void *, u_char *, int); + void (*write_fifo) (void *, u_char *, int); + void *hw; + int slot; /* multiport card channel slot */ + struct timer_list timer; + /* receive data */ + struct sk_buff *rx_skb; + int maxlen; + /* send data */ + struct sk_buff *next_skb; + struct sk_buff *tx_skb; + struct sk_buff_head rqueue; + int rcount; + int tx_idx; + int debug; + /* statistics */ + int err_crc; + int err_tx; + int err_rx; +}; + +extern int mISDN_initdchannel(struct dchannel *, int, void *); +extern int mISDN_initbchannel(struct bchannel *, int); +extern int mISDN_freedchannel(struct dchannel *); +extern int mISDN_freebchannel(struct bchannel *); +extern void queue_ch_frame(struct mISDNchannel *, u_int, + int, struct sk_buff *); +extern int dchannel_senddata(struct dchannel *, struct sk_buff *); +extern int bchannel_senddata(struct bchannel *, struct sk_buff *); +extern void recv_Dchannel(struct dchannel *); +extern void recv_Bchannel(struct bchannel *); +extern void recv_Dchannel_skb(struct dchannel *, struct sk_buff *); +extern void recv_Bchannel_skb(struct bchannel *, struct sk_buff *); +extern void confirm_Bsend(struct bchannel *bch); +extern int get_next_bframe(struct bchannel *); +extern int get_next_dframe(struct dchannel *); + +#endif diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h new file mode 100644 index 000000000000..5c948f337817 --- /dev/null +++ b/include/linux/mISDNif.h @@ -0,0 +1,487 @@ +/* + * + * Author Karsten Keil + * + * Copyright 2008 by Karsten Keil + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE + * version 2.1 as published by the Free Software Foundation. + * + * This code 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 LESSER GENERAL PUBLIC LICENSE for more details. + * + */ + +#ifndef mISDNIF_H +#define mISDNIF_H + +#include +#include +#include +#include + +/* + * ABI Version 32 bit + * + * <8 bit> Major version + * - changed if any interface become backwards incompatible + * + * <8 bit> Minor version + * - changed if any interface is extended but backwards compatible + * + * <16 bit> Release number + * - should be incremented on every checkin + */ +#define MISDN_MAJOR_VERSION 1 +#define MISDN_MINOR_VERSION 0 +#define MISDN_RELEASE 18 + +/* primitives for information exchange + * generell format + * <16 bit 0 > + * <8 bit command> + * BIT 8 = 1 LAYER private + * BIT 7 = 1 answer + * BIT 6 = 1 DATA + * <8 bit target layer mask> + * + * Layer = 00 is reserved for general commands + Layer = 01 L2 -> HW + Layer = 02 HW -> L2 + Layer = 04 L3 -> L2 + Layer = 08 L2 -> L3 + * Layer = FF is reserved for broadcast commands + */ + +#define MISDN_CMDMASK 0xff00 +#define MISDN_LAYERMASK 0x00ff + +/* generell commands */ +#define OPEN_CHANNEL 0x0100 +#define CLOSE_CHANNEL 0x0200 +#define CONTROL_CHANNEL 0x0300 +#define CHECK_DATA 0x0400 + +/* layer 2 -> layer 1 */ +#define PH_ACTIVATE_REQ 0x0101 +#define PH_DEACTIVATE_REQ 0x0201 +#define PH_DATA_REQ 0x2001 +#define MPH_ACTIVATE_REQ 0x0501 +#define MPH_DEACTIVATE_REQ 0x0601 +#define MPH_INFORMATION_REQ 0x0701 +#define PH_CONTROL_REQ 0x0801 + +/* layer 1 -> layer 2 */ +#define PH_ACTIVATE_IND 0x0102 +#define PH_ACTIVATE_CNF 0x4102 +#define PH_DEACTIVATE_IND 0x0202 +#define PH_DEACTIVATE_CNF 0x4202 +#define PH_DATA_IND 0x2002 +#define MPH_ACTIVATE_IND 0x0502 +#define MPH_DEACTIVATE_IND 0x0602 +#define MPH_INFORMATION_IND 0x0702 +#define PH_DATA_CNF 0x6002 +#define PH_CONTROL_IND 0x0802 +#define PH_CONTROL_CNF 0x4802 + +/* layer 3 -> layer 2 */ +#define DL_ESTABLISH_REQ 0x1004 +#define DL_RELEASE_REQ 0x1104 +#define DL_DATA_REQ 0x3004 +#define DL_UNITDATA_REQ 0x3104 +#define DL_INFORMATION_REQ 0x0004 + +/* layer 2 -> layer 3 */ +#define DL_ESTABLISH_IND 0x1008 +#define DL_ESTABLISH_CNF 0x5008 +#define DL_RELEASE_IND 0x1108 +#define DL_RELEASE_CNF 0x5108 +#define DL_DATA_IND 0x3008 +#define DL_UNITDATA_IND 0x3108 +#define DL_INFORMATION_IND 0x0008 + +/* intern layer 2 managment */ +#define MDL_ASSIGN_REQ 0x1804 +#define MDL_ASSIGN_IND 0x1904 +#define MDL_REMOVE_REQ 0x1A04 +#define MDL_REMOVE_IND 0x1B04 +#define MDL_STATUS_UP_IND 0x1C04 +#define MDL_STATUS_DOWN_IND 0x1D04 +#define MDL_STATUS_UI_IND 0x1E04 +#define MDL_ERROR_IND 0x1F04 +#define MDL_ERROR_RSP 0x5F04 + +/* DL_INFORMATION_IND types */ +#define DL_INFO_L2_CONNECT 0x0001 +#define DL_INFO_L2_REMOVED 0x0002 + +/* PH_CONTROL types */ +/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */ +#define DTMF_TONE_VAL 0x2000 +#define DTMF_TONE_MASK 0x007F +#define DTMF_TONE_START 0x2100 +#define DTMF_TONE_STOP 0x2200 +#define DTMF_HFC_COEF 0x4000 +#define DSP_CONF_JOIN 0x2403 +#define DSP_CONF_SPLIT 0x2404 +#define DSP_RECEIVE_OFF 0x2405 +#define DSP_RECEIVE_ON 0x2406 +#define DSP_ECHO_ON 0x2407 +#define DSP_ECHO_OFF 0x2408 +#define DSP_MIX_ON 0x2409 +#define DSP_MIX_OFF 0x240a +#define DSP_DELAY 0x240b +#define DSP_JITTER 0x240c +#define DSP_TXDATA_ON 0x240d +#define DSP_TXDATA_OFF 0x240e +#define DSP_TX_DEJITTER 0x240f +#define DSP_TX_DEJ_OFF 0x2410 +#define DSP_TONE_PATT_ON 0x2411 +#define DSP_TONE_PATT_OFF 0x2412 +#define DSP_VOL_CHANGE_TX 0x2413 +#define DSP_VOL_CHANGE_RX 0x2414 +#define DSP_BF_ENABLE_KEY 0x2415 +#define DSP_BF_DISABLE 0x2416 +#define DSP_BF_ACCEPT 0x2416 +#define DSP_BF_REJECT 0x2417 +#define DSP_PIPELINE_CFG 0x2418 +#define HFC_VOL_CHANGE_TX 0x2601 +#define HFC_VOL_CHANGE_RX 0x2602 +#define HFC_SPL_LOOP_ON 0x2603 +#define HFC_SPL_LOOP_OFF 0x2604 + +/* DSP_TONE_PATT_ON parameter */ +#define TONE_OFF 0x0000 +#define TONE_GERMAN_DIALTONE 0x0001 +#define TONE_GERMAN_OLDDIALTONE 0x0002 +#define TONE_AMERICAN_DIALTONE 0x0003 +#define TONE_GERMAN_DIALPBX 0x0004 +#define TONE_GERMAN_OLDDIALPBX 0x0005 +#define TONE_AMERICAN_DIALPBX 0x0006 +#define TONE_GERMAN_RINGING 0x0007 +#define TONE_GERMAN_OLDRINGING 0x0008 +#define TONE_AMERICAN_RINGPBX 0x000b +#define TONE_GERMAN_RINGPBX 0x000c +#define TONE_GERMAN_OLDRINGPBX 0x000d +#define TONE_AMERICAN_RINGING 0x000e +#define TONE_GERMAN_BUSY 0x000f +#define TONE_GERMAN_OLDBUSY 0x0010 +#define TONE_AMERICAN_BUSY 0x0011 +#define TONE_GERMAN_HANGUP 0x0012 +#define TONE_GERMAN_OLDHANGUP 0x0013 +#define TONE_AMERICAN_HANGUP 0x0014 +#define TONE_SPECIAL_INFO 0x0015 +#define TONE_GERMAN_GASSENBESETZT 0x0016 +#define TONE_GERMAN_AUFSCHALTTON 0x0016 + +/* MPH_INFORMATION_IND */ +#define L1_SIGNAL_LOS_OFF 0x0010 +#define L1_SIGNAL_LOS_ON 0x0011 +#define L1_SIGNAL_AIS_OFF 0x0012 +#define L1_SIGNAL_AIS_ON 0x0013 +#define L1_SIGNAL_RDI_OFF 0x0014 +#define L1_SIGNAL_RDI_ON 0x0015 +#define L1_SIGNAL_SLIP_RX 0x0020 +#define L1_SIGNAL_SLIP_TX 0x0021 + +/* + * protocol ids + * D channel 1-31 + * B channel 33 - 63 + */ + +#define ISDN_P_NONE 0 +#define ISDN_P_BASE 0 +#define ISDN_P_TE_S0 0x01 +#define ISDN_P_NT_S0 0x02 +#define ISDN_P_TE_E1 0x03 +#define ISDN_P_NT_E1 0x04 +#define ISDN_P_LAPD_TE 0x10 +#define ISDN_P_LAPD_NT 0x11 + +#define ISDN_P_B_MASK 0x1f +#define ISDN_P_B_START 0x20 + +#define ISDN_P_B_RAW 0x21 +#define ISDN_P_B_HDLC 0x22 +#define ISDN_P_B_X75SLP 0x23 +#define ISDN_P_B_L2DTMF 0x24 +#define ISDN_P_B_L2DSP 0x25 +#define ISDN_P_B_L2DSPHDLC 0x26 + +#define OPTION_L2_PMX 1 +#define OPTION_L2_PTP 2 +#define OPTION_L2_FIXEDTEI 3 +#define OPTION_L2_CLEANUP 4 + +/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */ +#define MISDN_MAX_IDLEN 20 + +struct mISDNhead { + unsigned int prim; + unsigned int id; +} __attribute__((packed)); + +#define MISDN_HEADER_LEN sizeof(struct mISDNhead) +#define MAX_DATA_SIZE 2048 +#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN) +#define MAX_DFRAME_LEN 260 + +#define MISDN_ID_ADDR_MASK 0xFFFF +#define MISDN_ID_TEI_MASK 0xFF00 +#define MISDN_ID_SAPI_MASK 0x00FF +#define MISDN_ID_TEI_ANY 0x7F00 + +#define MISDN_ID_ANY 0xFFFF +#define MISDN_ID_NONE 0xFFFE + +#define GROUP_TEI 127 +#define TEI_SAPI 63 +#define CTRL_SAPI 0 + +#define MISDN_CHMAP_SIZE 4 + +#define SOL_MISDN 0 + +struct sockaddr_mISDN { + sa_family_t family; + unsigned char dev; + unsigned char channel; + unsigned char sapi; + unsigned char tei; +}; + +/* timer device ioctl */ +#define IMADDTIMER _IOR('I', 64, int) +#define IMDELTIMER _IOR('I', 65, int) +/* socket ioctls */ +#define IMGETVERSION _IOR('I', 66, int) +#define IMGETCOUNT _IOR('I', 67, int) +#define IMGETDEVINFO _IOR('I', 68, int) +#define IMCTRLREQ _IOR('I', 69, int) +#define IMCLEAR_L2 _IOR('I', 70, int) + +struct mISDNversion { + unsigned char major; + unsigned char minor; + unsigned short release; +}; + +struct mISDN_devinfo { + u_int id; + u_int Dprotocols; + u_int Bprotocols; + u_int protocol; + u_long channelmap[MISDN_CHMAP_SIZE]; + u_int nrbchan; + char name[MISDN_MAX_IDLEN]; +}; + +/* CONTROL_CHANNEL parameters */ +#define MISDN_CTRL_GETOP 0x0000 +#define MISDN_CTRL_LOOP 0x0001 +#define MISDN_CTRL_CONNECT 0x0002 +#define MISDN_CTRL_DISCONNECT 0x0004 +#define MISDN_CTRL_PCMCONNECT 0x0010 +#define MISDN_CTRL_PCMDISCONNECT 0x0020 +#define MISDN_CTRL_SETPEER 0x0040 +#define MISDN_CTRL_UNSETPEER 0x0080 +#define MISDN_CTRL_RX_OFF 0x0100 +#define MISDN_CTRL_HW_FEATURES_OP 0x2000 +#define MISDN_CTRL_HW_FEATURES 0x2001 +#define MISDN_CTRL_HFC_OP 0x4000 +#define MISDN_CTRL_HFC_PCM_CONN 0x4001 +#define MISDN_CTRL_HFC_PCM_DISC 0x4002 +#define MISDN_CTRL_HFC_CONF_JOIN 0x4003 +#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004 +#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005 +#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006 +#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007 +#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008 + + +/* socket options */ +#define MISDN_TIME_STAMP 0x0001 + +struct mISDN_ctrl_req { + int op; + int channel; + int p1; + int p2; +}; + +/* muxer options */ +#define MISDN_OPT_ALL 1 +#define MISDN_OPT_TEIMGR 2 + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include + +#define DEBUG_CORE 0x000000ff +#define DEBUG_CORE_FUNC 0x00000002 +#define DEBUG_SOCKET 0x00000004 +#define DEBUG_MANAGER 0x00000008 +#define DEBUG_SEND_ERR 0x00000010 +#define DEBUG_MSG_THREAD 0x00000020 +#define DEBUG_QUEUE_FUNC 0x00000040 +#define DEBUG_L1 0x0000ff00 +#define DEBUG_L1_FSM 0x00000200 +#define DEBUG_L2 0x00ff0000 +#define DEBUG_L2_FSM 0x00020000 +#define DEBUG_L2_CTRL 0x00040000 +#define DEBUG_L2_RECV 0x00080000 +#define DEBUG_L2_TEI 0x00100000 +#define DEBUG_L2_TEIFSM 0x00200000 +#define DEBUG_TIMER 0x01000000 + +#define mISDN_HEAD_P(s) ((struct mISDNhead *)&s->cb[0]) +#define mISDN_HEAD_PRIM(s) (((struct mISDNhead *)&s->cb[0])->prim) +#define mISDN_HEAD_ID(s) (((struct mISDNhead *)&s->cb[0])->id) + +/* socket states */ +#define MISDN_OPEN 1 +#define MISDN_BOUND 2 +#define MISDN_CLOSED 3 + +struct mISDNchannel; +struct mISDNdevice; +struct mISDNstack; + +struct channel_req { + u_int protocol; + struct sockaddr_mISDN adr; + struct mISDNchannel *ch; +}; + +typedef int (ctrl_func_t)(struct mISDNchannel *, u_int, void *); +typedef int (send_func_t)(struct mISDNchannel *, struct sk_buff *); +typedef int (create_func_t)(struct channel_req *); + +struct Bprotocol { + struct list_head list; + char *name; + u_int Bprotocols; + create_func_t *create; +}; + +struct mISDNchannel { + struct list_head list; + u_int protocol; + u_int nr; + u_long opt; + u_int addr; + struct mISDNstack *st; + struct mISDNchannel *peer; + send_func_t *send; + send_func_t *recv; + ctrl_func_t *ctrl; +}; + +struct mISDN_sock_list { + struct hlist_head head; + rwlock_t lock; +}; + +struct mISDN_sock { + struct sock sk; + struct mISDNchannel ch; + u_int cmask; + struct mISDNdevice *dev; +}; + + + +struct mISDNdevice { + struct mISDNchannel D; + u_int id; + char name[MISDN_MAX_IDLEN]; + u_int Dprotocols; + u_int Bprotocols; + u_int nrbchan; + u_long channelmap[MISDN_CHMAP_SIZE]; + struct list_head bchannels; + struct mISDNchannel *teimgr; + struct device dev; +}; + +struct mISDNstack { + u_long status; + struct mISDNdevice *dev; + struct task_struct *thread; + struct completion *notify; + wait_queue_head_t workq; + struct sk_buff_head msgq; + struct list_head layer2; + struct mISDNchannel *layer1; + struct mISDNchannel own; + struct mutex lmutex; /* protect lists */ + struct mISDN_sock_list l1sock; +#ifdef MISDN_MSG_STATS + u_int msg_cnt; + u_int sleep_cnt; + u_int stopped_cnt; +#endif +}; + +/* global alloc/queue dunctions */ + +static inline struct sk_buff * +mI_alloc_skb(unsigned int len, gfp_t gfp_mask) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + MISDN_HEADER_LEN, gfp_mask); + if (likely(skb)) + skb_reserve(skb, MISDN_HEADER_LEN); + return skb; +} + +static inline struct sk_buff * +_alloc_mISDN_skb(u_int prim, u_int id, u_int len, void *dp, gfp_t gfp_mask) +{ + struct sk_buff *skb = mI_alloc_skb(len, gfp_mask); + struct mISDNhead *hh; + + if (!skb) + return NULL; + if (len) + memcpy(skb_put(skb, len), dp, len); + hh = mISDN_HEAD_P(skb); + hh->prim = prim; + hh->id = id; + return skb; +} + +static inline void +_queue_data(struct mISDNchannel *ch, u_int prim, + u_int id, u_int len, void *dp, gfp_t gfp_mask) +{ + struct sk_buff *skb; + + if (!ch->peer) + return; + skb = _alloc_mISDN_skb(prim, id, len, dp, gfp_mask); + if (!skb) + return; + if (ch->recv(ch->peer, skb)) + dev_kfree_skb(skb); +} + +/* global register/unregister functions */ + +extern int mISDN_register_device(struct mISDNdevice *, char *name); +extern void mISDN_unregister_device(struct mISDNdevice *); +extern int mISDN_register_Bprotocol(struct Bprotocol *); +extern void mISDN_unregister_Bprotocol(struct Bprotocol *); + +extern void set_channel_address(struct mISDNchannel *, u_int, u_int); + +#endif /* __KERNEL__ */ +#endif /* mISDNIF_H */ -- cgit From 960366cf8dbb3359afaca30cf7fdbf69a6d6dda7 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Sun, 27 Jul 2008 01:56:38 +0200 Subject: Add mISDN DSP Enable support for digital audio processing capability. This module may be used for special applications that require cross connecting of bchannels, conferencing, dtmf decoding echo cancelation, tone generation, and Blowfish encryption and decryption. It may use hardware features if available. Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/Kconfig | 18 + drivers/isdn/mISDN/Makefile | 2 + drivers/isdn/mISDN/dsp.h | 263 ++++++ drivers/isdn/mISDN/dsp_audio.c | 434 +++++++++ drivers/isdn/mISDN/dsp_biquad.h | 65 ++ drivers/isdn/mISDN/dsp_blowfish.c | 672 +++++++++++++ drivers/isdn/mISDN/dsp_cmx.c | 1886 +++++++++++++++++++++++++++++++++++++ drivers/isdn/mISDN/dsp_core.c | 1191 +++++++++++++++++++++++ drivers/isdn/mISDN/dsp_dtmf.c | 303 ++++++ drivers/isdn/mISDN/dsp_ecdis.h | 110 +++ drivers/isdn/mISDN/dsp_hwec.c | 138 +++ drivers/isdn/mISDN/dsp_hwec.h | 10 + drivers/isdn/mISDN/dsp_pipeline.c | 348 +++++++ drivers/isdn/mISDN/dsp_tones.c | 551 +++++++++++ include/linux/mISDNdsp.h | 37 + 15 files changed, 6028 insertions(+) create mode 100644 drivers/isdn/mISDN/dsp.h create mode 100644 drivers/isdn/mISDN/dsp_audio.c create mode 100644 drivers/isdn/mISDN/dsp_biquad.h create mode 100644 drivers/isdn/mISDN/dsp_blowfish.c create mode 100644 drivers/isdn/mISDN/dsp_cmx.c create mode 100644 drivers/isdn/mISDN/dsp_core.c create mode 100644 drivers/isdn/mISDN/dsp_dtmf.c create mode 100644 drivers/isdn/mISDN/dsp_ecdis.h create mode 100644 drivers/isdn/mISDN/dsp_hwec.c create mode 100644 drivers/isdn/mISDN/dsp_hwec.h create mode 100644 drivers/isdn/mISDN/dsp_pipeline.c create mode 100644 drivers/isdn/mISDN/dsp_tones.c create mode 100644 include/linux/mISDNdsp.h (limited to 'include/linux') diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig index 231bd0d08316..6a97e86e7f21 100644 --- a/drivers/isdn/mISDN/Kconfig +++ b/drivers/isdn/mISDN/Kconfig @@ -7,3 +7,21 @@ menuconfig MISDN help Enable support for the modular ISDN driver. +if MISDN != n + +config MISDN_DSP + tristate "Digital Audio Processing of transparent data" + depends on MISDN + help + Enable support for digital audio processing capability. + This module may be used for special applications that require + cross connecting of bchannels, conferencing, dtmf decoding + echo cancelation, tone generation, and Blowfish encryption and + decryption. + It may use hardware features if available. + E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu + and get more informations about this module and it's usage. + If unsure, say 'N'. + + source "drivers/isdn/hardware/mISDN/Kconfig" +endif #MISDN diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile index 87c563d33612..7f1a21804208 100644 --- a/drivers/isdn/mISDN/Makefile +++ b/drivers/isdn/mISDN/Makefile @@ -3,7 +3,9 @@ # obj-$(CONFIG_MISDN) += mISDN_core.o +obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o # multi objects mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o +mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h new file mode 100644 index 000000000000..6c3fed6b8d4f --- /dev/null +++ b/drivers/isdn/mISDN/dsp.h @@ -0,0 +1,263 @@ +/* + * Audio support data for ISDN4Linux. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#define DEBUG_DSP_CTRL 0x0001 +#define DEBUG_DSP_CORE 0x0002 +#define DEBUG_DSP_DTMF 0x0004 +#define DEBUG_DSP_CMX 0x0010 +#define DEBUG_DSP_TONE 0x0020 +#define DEBUG_DSP_BLOWFISH 0x0040 +#define DEBUG_DSP_DELAY 0x0100 +#define DEBUG_DSP_DTMFCOEFF 0x8000 /* heavy output */ + +/* options may be: + * + * bit 0 = use ulaw instead of alaw + * bit 1 = enable hfc hardware accelleration for all channels + * + */ +#define DSP_OPT_ULAW (1<<0) +#define DSP_OPT_NOHARDWARE (1<<1) + +#include +#include + +#include "dsp_ecdis.h" + +extern int dsp_options; +extern int dsp_debug; +extern int dsp_poll; +extern int dsp_tics; +extern spinlock_t dsp_lock; +extern struct work_struct dsp_workq; +extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */ + +/*************** + * audio stuff * + ***************/ + +extern s32 dsp_audio_alaw_to_s32[256]; +extern s32 dsp_audio_ulaw_to_s32[256]; +extern s32 *dsp_audio_law_to_s32; +extern u8 dsp_audio_s16_to_law[65536]; +extern u8 dsp_audio_alaw_to_ulaw[256]; +extern u8 dsp_audio_mix_law[65536]; +extern u8 dsp_audio_seven2law[128]; +extern u8 dsp_audio_law2seven[256]; +extern void dsp_audio_generate_law_tables(void); +extern void dsp_audio_generate_s2law_table(void); +extern void dsp_audio_generate_seven(void); +extern void dsp_audio_generate_mix_table(void); +extern void dsp_audio_generate_ulaw_samples(void); +extern void dsp_audio_generate_volume_changes(void); +extern u8 dsp_silence; + + +/************* + * cmx stuff * + *************/ + +#define MAX_POLL 256 /* maximum number of send-chunks */ + +#define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */ +#define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */ +#define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */ + +/* how many seconds will we check the lowest delay until the jitter buffer + is reduced by that delay */ +#define MAX_SECONDS_JITTER_CHECK 5 + +extern struct timer_list dsp_spl_tl; +extern u32 dsp_spl_jiffies; + +/* the structure of conferences: + * + * each conference has a unique number, given by user space. + * the conferences are linked in a chain. + * each conference has members linked in a chain. + * each dsplayer points to a member, each member points to a dsplayer. + */ + +/* all members within a conference (this is linked 1:1 with the dsp) */ +struct dsp; +struct dsp_conf_member { + struct list_head list; + struct dsp *dsp; +}; + +/* the list of all conferences */ +struct dsp_conf { + struct list_head list; + u32 id; + /* all cmx stacks with the same ID are + connected */ + struct list_head mlist; + int software; /* conf is processed by software */ + int hardware; /* conf is processed by hardware */ + /* note: if both unset, has only one member */ +}; + + +/************** + * DTMF stuff * + **************/ + +#define DSP_DTMF_NPOINTS 102 + +#define ECHOCAN_BUFLEN (4*128) + +struct dsp_dtmf { + int treshold; /* above this is dtmf (square of) */ + int software; /* dtmf uses software decoding */ + int hardware; /* dtmf uses hardware decoding */ + int size; /* number of bytes in buffer */ + signed short buffer[DSP_DTMF_NPOINTS]; + /* buffers one full dtmf frame */ + u8 lastwhat, lastdigit; + int count; + u8 digits[16]; /* just the dtmf result */ +}; + + +/****************** + * pipeline stuff * + ******************/ +struct dsp_pipeline { + rwlock_t lock; + struct list_head list; + int inuse; +}; + +/*************** + * tones stuff * + ***************/ + +struct dsp_tone { + int software; /* tones are generated by software */ + int hardware; /* tones are generated by hardware */ + int tone; + void *pattern; + int count; + int index; + struct timer_list tl; +}; + +/***************** + * general stuff * + *****************/ + +struct dsp { + struct list_head list; + struct mISDNchannel ch; + struct mISDNchannel *up; + unsigned char name[64]; + int b_active; + int echo; /* echo is enabled */ + int rx_disabled; /* what the user wants */ + int rx_is_off; /* what the card is */ + int tx_mix; + struct dsp_tone tone; + struct dsp_dtmf dtmf; + int tx_volume, rx_volume; + + /* queue for sending frames */ + struct work_struct workq; + struct sk_buff_head sendq; + int hdlc; /* if mode is hdlc */ + int data_pending; /* currently an unconfirmed frame */ + + /* conference stuff */ + u32 conf_id; + struct dsp_conf *conf; + struct dsp_conf_member + *member; + + /* buffer stuff */ + int rx_W; /* current write pos for data without timestamp */ + int rx_R; /* current read pos for transmit clock */ + int rx_init; /* if set, pointers will be adjusted first */ + int tx_W; /* current write pos for transmit data */ + int tx_R; /* current read pos for transmit clock */ + int rx_delay[MAX_SECONDS_JITTER_CHECK]; + int tx_delay[MAX_SECONDS_JITTER_CHECK]; + u8 tx_buff[CMX_BUFF_SIZE]; + u8 rx_buff[CMX_BUFF_SIZE]; + int last_tx; /* if set, we transmitted last poll interval */ + int cmx_delay; /* initial delay of buffers, + or 0 for dynamic jitter buffer */ + int tx_dejitter; /* if set, dejitter tx buffer */ + int tx_data; /* enables tx-data of CMX to upper layer */ + + /* hardware stuff */ + struct dsp_features features; + int features_rx_off; /* set if rx_off is featured */ + int pcm_slot_rx; /* current PCM slot (or -1) */ + int pcm_bank_rx; + int pcm_slot_tx; + int pcm_bank_tx; + int hfc_conf; /* unique id of current conference (or -1) */ + + /* encryption stuff */ + int bf_enable; + u32 bf_p[18]; + u32 bf_s[1024]; + int bf_crypt_pos; + u8 bf_data_in[9]; + u8 bf_crypt_out[9]; + int bf_decrypt_in_pos; + int bf_decrypt_out_pos; + u8 bf_crypt_inring[16]; + u8 bf_data_out[9]; + int bf_sync; + + struct dsp_pipeline + pipeline; +}; + +/* functions */ + +extern void dsp_change_volume(struct sk_buff *skb, int volume); + +extern struct list_head dsp_ilist; +extern struct list_head conf_ilist; +extern void dsp_cmx_debug(struct dsp *dsp); +extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp); +extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id); +extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb); +extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb); +extern void dsp_cmx_send(void *arg); +extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb); +extern int dsp_cmx_del_conf_member(struct dsp *dsp); +extern int dsp_cmx_del_conf(struct dsp_conf *conf); + +extern void dsp_dtmf_goertzel_init(struct dsp *dsp); +extern void dsp_dtmf_hardware(struct dsp *dsp); +extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, + int fmt); + +extern int dsp_tone(struct dsp *dsp, int tone); +extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len); +extern void dsp_tone_timeout(void *arg); + +extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len); +extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len); +extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen); +extern void dsp_bf_cleanup(struct dsp *dsp); + +extern int dsp_pipeline_module_init(void); +extern void dsp_pipeline_module_exit(void); +extern int dsp_pipeline_init(struct dsp_pipeline *pipeline); +extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline); +extern int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg); +extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, + int len); +extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, + int len); + diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c new file mode 100644 index 000000000000..1c2dd5694773 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_audio.c @@ -0,0 +1,434 @@ +/* + * Audio support data for mISDN_dsp. + * + * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu) + * Rewritten by Peter + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include +#include "core.h" +#include "dsp.h" + +/* ulaw[unsigned char] -> signed 16-bit */ +s32 dsp_audio_ulaw_to_s32[256]; +/* alaw[unsigned char] -> signed 16-bit */ +s32 dsp_audio_alaw_to_s32[256]; + +s32 *dsp_audio_law_to_s32; +EXPORT_SYMBOL(dsp_audio_law_to_s32); + +/* signed 16-bit -> law */ +u8 dsp_audio_s16_to_law[65536]; +EXPORT_SYMBOL(dsp_audio_s16_to_law); + +/* alaw -> ulaw */ +u8 dsp_audio_alaw_to_ulaw[256]; +/* ulaw -> alaw */ +u8 dsp_audio_ulaw_to_alaw[256]; +u8 dsp_silence; + + +/***************************************************** + * generate table for conversion of s16 to alaw/ulaw * + *****************************************************/ + +#define AMI_MASK 0x55 + +static inline unsigned char linear2alaw(short int linear) +{ + int mask; + int seg; + int pcm_val; + static int seg_end[8] = { + 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF + }; + + pcm_val = linear; + if (pcm_val >= 0) { + /* Sign (7th) bit = 1 */ + mask = AMI_MASK | 0x80; + } else { + /* Sign bit = 0 */ + mask = AMI_MASK; + pcm_val = -pcm_val; + } + + /* Convert the scaled magnitude to segment number. */ + for (seg = 0; seg < 8; seg++) { + if (pcm_val <= seg_end[seg]) + break; + } + /* Combine the sign, segment, and quantization bits. */ + return ((seg << 4) | + ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask; +} + + +static inline short int alaw2linear(unsigned char alaw) +{ + int i; + int seg; + + alaw ^= AMI_MASK; + i = ((alaw & 0x0F) << 4) + 8 /* rounding error */; + seg = (((int) alaw & 0x70) >> 4); + if (seg) + i = (i + 0x100) << (seg - 1); + return (short int) ((alaw & 0x80) ? i : -i); +} + +static inline short int ulaw2linear(unsigned char ulaw) +{ + short mu, e, f, y; + static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764}; + + mu = 255 - ulaw; + e = (mu & 0x70) / 16; + f = mu & 0x0f; + y = f * (1 << (e + 3)); + y += etab[e]; + if (mu & 0x80) + y = -y; + return y; +} + +#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */ + +static unsigned char linear2ulaw(short sample) +{ + static int exp_lut[256] = { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; + int sign, exponent, mantissa; + unsigned char ulawbyte; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80; /* set aside the sign */ + if (sign != 0) + sample = -sample; /* get magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[(sample >> 7) & 0xFF]; + mantissa = (sample >> (exponent + 3)) & 0x0F; + ulawbyte = ~(sign | (exponent << 4) | mantissa); + + return ulawbyte; +} + +static int reverse_bits(int i) +{ + int z, j; + z = 0; + + for (j = 0; j < 8; j++) { + if ((i & (1 << j)) != 0) + z |= 1 << (7 - j); + } + return z; +} + + +void dsp_audio_generate_law_tables(void) +{ + int i; + for (i = 0; i < 256; i++) + dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i)); + + for (i = 0; i < 256; i++) + dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(i)); + + for (i = 0; i < 256; i++) { + dsp_audio_alaw_to_ulaw[i] = + linear2ulaw(dsp_audio_alaw_to_s32[i]); + dsp_audio_ulaw_to_alaw[i] = + linear2alaw(dsp_audio_ulaw_to_s32[i]); + } +} + +void +dsp_audio_generate_s2law_table(void) +{ + int i; + + if (dsp_options & DSP_OPT_ULAW) { + /* generating ulaw-table */ + for (i = -32768; i < 32768; i++) { + dsp_audio_s16_to_law[i & 0xffff] = + reverse_bits(linear2ulaw(i)); + } + } else { + /* generating alaw-table */ + for (i = -32768; i < 32768; i++) { + dsp_audio_s16_to_law[i & 0xffff] = + reverse_bits(linear2alaw(i)); + } + } +} + + +/* + * the seven bit sample is the number of every second alaw-sample ordered by + * aplitude. 0x00 is negative, 0x7f is positive amplitude. + */ +u8 dsp_audio_seven2law[128]; +u8 dsp_audio_law2seven[256]; + +/******************************************************************** + * generate table for conversion law from/to 7-bit alaw-like sample * + ********************************************************************/ + +void +dsp_audio_generate_seven(void) +{ + int i, j, k; + u8 spl; + u8 sorted_alaw[256]; + + /* generate alaw table, sorted by the linear value */ + for (i = 0; i < 256; i++) { + j = 0; + for (k = 0; k < 256; k++) { + if (dsp_audio_alaw_to_s32[k] + < dsp_audio_alaw_to_s32[i]) { + j++; + } + } + sorted_alaw[j] = i; + } + + /* generate tabels */ + for (i = 0; i < 256; i++) { + /* spl is the source: the law-sample (converted to alaw) */ + spl = i; + if (dsp_options & DSP_OPT_ULAW) + spl = dsp_audio_ulaw_to_alaw[i]; + /* find the 7-bit-sample */ + for (j = 0; j < 256; j++) { + if (sorted_alaw[j] == spl) + break; + } + /* write 7-bit audio value */ + dsp_audio_law2seven[i] = j >> 1; + } + for (i = 0; i < 128; i++) { + spl = sorted_alaw[i << 1]; + if (dsp_options & DSP_OPT_ULAW) + spl = dsp_audio_alaw_to_ulaw[spl]; + dsp_audio_seven2law[i] = spl; + } +} + + +/* mix 2*law -> law */ +u8 dsp_audio_mix_law[65536]; + +/****************************************************** + * generate mix table to mix two law samples into one * + ******************************************************/ + +void +dsp_audio_generate_mix_table(void) +{ + int i, j; + s32 sample; + + i = 0; + while (i < 256) { + j = 0; + while (j < 256) { + sample = dsp_audio_law_to_s32[i]; + sample += dsp_audio_law_to_s32[j]; + if (sample > 32767) + sample = 32767; + if (sample < -32768) + sample = -32768; + dsp_audio_mix_law[(i<<8)|j] = + dsp_audio_s16_to_law[sample & 0xffff]; + j++; + } + i++; + } +} + + +/************************************* + * generate different volume changes * + *************************************/ + +static u8 dsp_audio_reduce8[256]; +static u8 dsp_audio_reduce7[256]; +static u8 dsp_audio_reduce6[256]; +static u8 dsp_audio_reduce5[256]; +static u8 dsp_audio_reduce4[256]; +static u8 dsp_audio_reduce3[256]; +static u8 dsp_audio_reduce2[256]; +static u8 dsp_audio_reduce1[256]; +static u8 dsp_audio_increase1[256]; +static u8 dsp_audio_increase2[256]; +static u8 dsp_audio_increase3[256]; +static u8 dsp_audio_increase4[256]; +static u8 dsp_audio_increase5[256]; +static u8 dsp_audio_increase6[256]; +static u8 dsp_audio_increase7[256]; +static u8 dsp_audio_increase8[256]; + +static u8 *dsp_audio_volume_change[16] = { + dsp_audio_reduce8, + dsp_audio_reduce7, + dsp_audio_reduce6, + dsp_audio_reduce5, + dsp_audio_reduce4, + dsp_audio_reduce3, + dsp_audio_reduce2, + dsp_audio_reduce1, + dsp_audio_increase1, + dsp_audio_increase2, + dsp_audio_increase3, + dsp_audio_increase4, + dsp_audio_increase5, + dsp_audio_increase6, + dsp_audio_increase7, + dsp_audio_increase8, +}; + +void +dsp_audio_generate_volume_changes(void) +{ + register s32 sample; + int i; + int num[] = { 110, 125, 150, 175, 200, 300, 400, 500 }; + int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 }; + + i = 0; + while (i < 256) { + dsp_audio_reduce8[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff]; + dsp_audio_reduce7[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff]; + dsp_audio_reduce6[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff]; + dsp_audio_reduce5[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff]; + dsp_audio_reduce4[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff]; + dsp_audio_reduce3[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff]; + dsp_audio_reduce2[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff]; + dsp_audio_reduce1[i] = dsp_audio_s16_to_law[ + (dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[0] / denum[0]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[1] / denum[1]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[2] / denum[2]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[3] / denum[3]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[4] / denum[4]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[5] / denum[5]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[6] / denum[6]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff]; + sample = dsp_audio_law_to_s32[i] * num[7] / denum[7]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff]; + + i++; + } +} + + +/************************************** + * change the volume of the given skb * + **************************************/ + +/* this is a helper function for changing volume of skb. the range may be + * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8 + */ +void +dsp_change_volume(struct sk_buff *skb, int volume) +{ + u8 *volume_change; + int i, ii; + u8 *p; + int shift; + + if (volume == 0) + return; + + /* get correct conversion table */ + if (volume < 0) { + shift = volume + 8; + if (shift < 0) + shift = 0; + } else { + shift = volume + 7; + if (shift > 15) + shift = 15; + } + volume_change = dsp_audio_volume_change[shift]; + i = 0; + ii = skb->len; + p = skb->data; + /* change volume */ + while (i < ii) { + *p = volume_change[*p]; + p++; + i++; + } +} + diff --git a/drivers/isdn/mISDN/dsp_biquad.h b/drivers/isdn/mISDN/dsp_biquad.h new file mode 100644 index 000000000000..038191bc45f5 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_biquad.h @@ -0,0 +1,65 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * biquad.h - General telephony bi-quad section routines (currently this just + * handles canonic/type 2 form) + * + * Written by Steve Underwood + * + * Copyright (C) 2001 Steve Underwood + * + * 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. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +struct biquad2_state { + int32_t gain; + int32_t a1; + int32_t a2; + int32_t b1; + int32_t b2; + + int32_t z1; + int32_t z2; +}; + +static inline void biquad2_init(struct biquad2_state *bq, + int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2) +{ + bq->gain = gain; + bq->a1 = a1; + bq->a2 = a2; + bq->b1 = b1; + bq->b2 = b2; + + bq->z1 = 0; + bq->z2 = 0; +} + +static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample) +{ + int32_t y; + int32_t z0; + + z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2; + y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2; + + bq->z2 = bq->z1; + bq->z1 = z0 >> 15; + y >>= 15; + return y; +} diff --git a/drivers/isdn/mISDN/dsp_blowfish.c b/drivers/isdn/mISDN/dsp_blowfish.c new file mode 100644 index 000000000000..18e411e95bba --- /dev/null +++ b/drivers/isdn/mISDN/dsp_blowfish.c @@ -0,0 +1,672 @@ +/* + * Blowfish encryption/decryption for mISDN_dsp. + * + * Copyright Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include "core.h" +#include "dsp.h" + +/* + * how to encode a sample stream to 64-bit blocks that will be encryped + * + * first of all, data is collected until a block of 9 samples are received. + * of course, a packet may have much more than 9 sample, but is may have + * not excacly the multiple of 9 samples. if there is a rest, the next + * received data will complete the block. + * + * the block is then converted to 9 uLAW samples without the least sigificant + * bit. the result is a 7-bit encoded sample. + * + * the samples will be reoganised to form 8 bytes of data: + * (5(6) means: encoded sample no. 5, bit 6) + * + * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6) + * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5) + * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4) + * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3) + * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2) + * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) + * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) + * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0) + * + * the missing bit 0 of the last byte is filled with some + * random noise, to fill all 8 bytes. + * + * the 8 bytes will be encrypted using blowfish. + * + * the result will be converted into 9 bytes. the bit 7 is used for + * checksumme (CS) for sync (0, 1) and for the last bit: + * (5(6) means: crypted byte 5, bit 6) + * + * 1 0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) + * 0 0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2) + * 0 1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3) + * 0 2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4) + * 0 3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5) + * CS 4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6) + * CS 5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7) + * CS 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0) + * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0) + * + * the checksum is used to detect transmission errors and frame drops. + * + * synchronisation of received block is done by shifting the upper bit of each + * byte (bit 7) to a shift register. if the rigister has the first five bits + * (10000), this is used to find the sync. only if sync has been found, the + * current block of 9 received bytes are decrypted. before that the check + * sum is calculated. if it is incorrect the block is dropped. + * this will avoid loud noise due to corrupt encrypted data. + * + * if the last block is corrupt, the current decoded block is repeated + * until a valid block has been received. + */ + +/* + * some blowfish parts are taken from the + * crypto-api for faster implementation + */ + +struct bf_ctx { + u32 p[18]; + u32 s[1024]; +}; + +static const u32 bf_pbox[16 + 2] = { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b, +}; + +static const u32 bf_sbox[256 * 4] = { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, +}; + +/* + * Round loop unrolling macros, S is a pointer to a S-Box array + * organized in 4 unsigned longs at a row. + */ +#define GET32_3(x) (((x) & 0xff)) +#define GET32_2(x) (((x) >> (8)) & (0xff)) +#define GET32_1(x) (((x) >> (16)) & (0xff)) +#define GET32_0(x) (((x) >> (24)) & (0xff)) + +#define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \ + S[512 + GET32_2(x)]) + S[768 + GET32_3(x)]) + +#define EROUND(a, b, n) do { b ^= P[n]; a ^= bf_F(b); } while (0) +#define DROUND(a, b, n) do { a ^= bf_F(b); b ^= P[n]; } while (0) + + +/* + * encrypt isdn data frame + * every block with 9 samples is encrypted + */ +void +dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len) +{ + int i = 0, j = dsp->bf_crypt_pos; + u8 *bf_data_in = dsp->bf_data_in; + u8 *bf_crypt_out = dsp->bf_crypt_out; + u32 *P = dsp->bf_p; + u32 *S = dsp->bf_s; + u32 yl, yr; + u32 cs; + u8 nibble; + + while (i < len) { + /* collect a block of 9 samples */ + if (j < 9) { + bf_data_in[j] = *data; + *data++ = bf_crypt_out[j++]; + i++; + continue; + } + j = 0; + /* transcode 9 samples xlaw to 8 bytes */ + yl = dsp_audio_law2seven[bf_data_in[0]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[1]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[2]]; + yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[3]]; + nibble = dsp_audio_law2seven[bf_data_in[4]]; + yr = nibble; + yl = (yl<<4) | (nibble>>3); + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[5]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[6]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[7]]; + yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[8]]; + yr = (yr<<1) | (bf_data_in[0] & 1); + + /* fill unused bit with random noise of audio input */ + /* encrypt */ + + EROUND(yr, yl, 0); + EROUND(yl, yr, 1); + EROUND(yr, yl, 2); + EROUND(yl, yr, 3); + EROUND(yr, yl, 4); + EROUND(yl, yr, 5); + EROUND(yr, yl, 6); + EROUND(yl, yr, 7); + EROUND(yr, yl, 8); + EROUND(yl, yr, 9); + EROUND(yr, yl, 10); + EROUND(yl, yr, 11); + EROUND(yr, yl, 12); + EROUND(yl, yr, 13); + EROUND(yr, yl, 14); + EROUND(yl, yr, 15); + yl ^= P[16]; + yr ^= P[17]; + + /* calculate 3-bit checksumme */ + cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) + ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) + ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) + ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) + ^ (yr>>28) ^ (yr>>31); + + /* + * transcode 8 crypted bytes to 9 data bytes with sync + * and checksum information + */ + bf_crypt_out[0] = (yl>>25) | 0x80; + bf_crypt_out[1] = (yl>>18) & 0x7f; + bf_crypt_out[2] = (yl>>11) & 0x7f; + bf_crypt_out[3] = (yl>>4) & 0x7f; + bf_crypt_out[4] = ((yl<<3) & 0x78) | ((yr>>29) & 0x07); + bf_crypt_out[5] = ((yr>>22) & 0x7f) | ((cs<<5) & 0x80); + bf_crypt_out[6] = ((yr>>15) & 0x7f) | ((cs<<6) & 0x80); + bf_crypt_out[7] = ((yr>>8) & 0x7f) | (cs<<7); + bf_crypt_out[8] = yr; + } + + /* write current count */ + dsp->bf_crypt_pos = j; + +} + + +/* + * decrypt isdn data frame + * every block with 9 bytes is decrypted + */ +void +dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len) +{ + int i = 0; + u8 j = dsp->bf_decrypt_in_pos; + u8 k = dsp->bf_decrypt_out_pos; + u8 *bf_crypt_inring = dsp->bf_crypt_inring; + u8 *bf_data_out = dsp->bf_data_out; + u16 sync = dsp->bf_sync; + u32 *P = dsp->bf_p; + u32 *S = dsp->bf_s; + u32 yl, yr; + u8 nibble; + u8 cs, cs0, cs1, cs2; + + while (i < len) { + /* + * shift upper bit and rotate data to buffer ring + * send current decrypted data + */ + sync = (sync<<1) | ((*data)>>7); + bf_crypt_inring[j++ & 15] = *data; + *data++ = bf_data_out[k++]; + i++; + if (k == 9) + k = 0; /* repeat if no sync has been found */ + /* check if not in sync */ + if ((sync&0x1f0) != 0x100) + continue; + j -= 9; + /* transcode receive data to 64 bit block of encrypted data */ + yl = bf_crypt_inring[j++ & 15]; + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */ + yr = nibble; + yl = (yl<<4) | (nibble>>3); + cs2 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs2 & 0x7f); + cs1 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs1 & 0x7f); + cs0 = bf_crypt_inring[j++ & 15]; + yr = (yr<<7) | (cs0 & 0x7f); + yr = (yr<<8) | bf_crypt_inring[j++ & 15]; + + /* calculate 3-bit checksumme */ + cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15) + ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30) + ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10) + ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25) + ^ (yr>>28) ^ (yr>>31); + + /* check if frame is valid */ + if ((cs&0x7) != (((cs2>>5)&4) | ((cs1>>6)&2) | (cs0 >> 7))) { + if (dsp_debug & DEBUG_DSP_BLOWFISH) + printk(KERN_DEBUG + "DSP BLOWFISH: received corrupt frame, " + "checksumme is not correct\n"); + continue; + } + + /* decrypt */ + yr ^= P[17]; + yl ^= P[16]; + DROUND(yl, yr, 15); + DROUND(yr, yl, 14); + DROUND(yl, yr, 13); + DROUND(yr, yl, 12); + DROUND(yl, yr, 11); + DROUND(yr, yl, 10); + DROUND(yl, yr, 9); + DROUND(yr, yl, 8); + DROUND(yl, yr, 7); + DROUND(yr, yl, 6); + DROUND(yl, yr, 5); + DROUND(yr, yl, 4); + DROUND(yl, yr, 3); + DROUND(yr, yl, 2); + DROUND(yl, yr, 1); + DROUND(yr, yl, 0); + + /* transcode 8 crypted bytes to 9 sample bytes */ + bf_data_out[0] = dsp_audio_seven2law[(yl>>25) & 0x7f]; + bf_data_out[1] = dsp_audio_seven2law[(yl>>18) & 0x7f]; + bf_data_out[2] = dsp_audio_seven2law[(yl>>11) & 0x7f]; + bf_data_out[3] = dsp_audio_seven2law[(yl>>4) & 0x7f]; + bf_data_out[4] = dsp_audio_seven2law[((yl<<3) & 0x78) | + ((yr>>29) & 0x07)]; + + bf_data_out[5] = dsp_audio_seven2law[(yr>>22) & 0x7f]; + bf_data_out[6] = dsp_audio_seven2law[(yr>>15) & 0x7f]; + bf_data_out[7] = dsp_audio_seven2law[(yr>>8) & 0x7f]; + bf_data_out[8] = dsp_audio_seven2law[(yr>>1) & 0x7f]; + k = 0; /* start with new decoded frame */ + } + + /* write current count and sync */ + dsp->bf_decrypt_in_pos = j; + dsp->bf_decrypt_out_pos = k; + dsp->bf_sync = sync; +} + + +/* used to encrypt S and P boxes */ +static inline void +encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src) +{ + u32 yl = src[0]; + u32 yr = src[1]; + + EROUND(yr, yl, 0); + EROUND(yl, yr, 1); + EROUND(yr, yl, 2); + EROUND(yl, yr, 3); + EROUND(yr, yl, 4); + EROUND(yl, yr, 5); + EROUND(yr, yl, 6); + EROUND(yl, yr, 7); + EROUND(yr, yl, 8); + EROUND(yl, yr, 9); + EROUND(yr, yl, 10); + EROUND(yl, yr, 11); + EROUND(yr, yl, 12); + EROUND(yl, yr, 13); + EROUND(yr, yl, 14); + EROUND(yl, yr, 15); + + yl ^= P[16]; + yr ^= P[17]; + + dst[0] = yr; + dst[1] = yl; +} + +/* + * initialize the dsp for encryption and decryption using the same key + * Calculates the blowfish S and P boxes for encryption and decryption. + * The margin of keylen must be 4-56 bytes. + * returns 0 if ok. + */ +int +dsp_bf_init(struct dsp *dsp, const u8 *key, uint keylen) +{ + short i, j, count; + u32 data[2], temp; + u32 *P = (u32 *)dsp->bf_p; + u32 *S = (u32 *)dsp->bf_s; + + if (keylen < 4 || keylen > 56) + return 1; + + /* Set dsp states */ + i = 0; + while (i < 9) { + dsp->bf_crypt_out[i] = 0xff; + dsp->bf_data_out[i] = dsp_silence; + i++; + } + dsp->bf_crypt_pos = 0; + dsp->bf_decrypt_in_pos = 0; + dsp->bf_decrypt_out_pos = 0; + dsp->bf_sync = 0x1ff; + dsp->bf_enable = 1; + + /* Copy the initialization s-boxes */ + for (i = 0, count = 0; i < 256; i++) + for (j = 0; j < 4; j++, count++) + S[count] = bf_sbox[count]; + + /* Set the p-boxes */ + for (i = 0; i < 16 + 2; i++) + P[i] = bf_pbox[i]; + + /* Actual subkey generation */ + for (j = 0, i = 0; i < 16 + 2; i++) { + temp = (((u32)key[j] << 24) | + ((u32)key[(j + 1) % keylen] << 16) | + ((u32)key[(j + 2) % keylen] << 8) | + ((u32)key[(j + 3) % keylen])); + + P[i] = P[i] ^ temp; + j = (j + 4) % keylen; + } + + data[0] = 0x00000000; + data[1] = 0x00000000; + + for (i = 0; i < 16 + 2; i += 2) { + encrypt_block(P, S, data, data); + + P[i] = data[0]; + P[i + 1] = data[1]; + } + + for (i = 0; i < 4; i++) { + for (j = 0, count = i * 256; j < 256; j += 2, count += 2) { + encrypt_block(P, S, data, data); + + S[count] = data[0]; + S[count + 1] = data[1]; + } + } + + return 0; +} + + +/* + * turn encryption off + */ +void +dsp_bf_cleanup(struct dsp *dsp) +{ + dsp->bf_enable = 0; +} diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c new file mode 100644 index 000000000000..e92b1ba4b45e --- /dev/null +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -0,0 +1,1886 @@ +/* + * Audio crossconnecting/conferrencing (hardware level). + * + * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +/* + * The process of adding and removing parties to/from a conference: + * + * There is a chain of struct dsp_conf which has one or more members in a chain + * of struct dsp_conf_member. + * + * After a party is added, the conference is checked for hardware capability. + * Also if a party is removed, the conference is checked again. + * + * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect + * 1-n = hardware-conference. The n will give the conference number. + * + * Depending on the change after removal or insertion of a party, hardware + * commands are given. + * + * The current solution is stored within the struct dsp_conf entry. + */ + +/* + * HOW THE CMX WORKS: + * + * There are 3 types of interaction: One member is alone, in this case only + * data flow from upper to lower layer is done. + * Two members will also exchange their data so they are crossconnected. + * Three or more members will be added in a conference and will hear each + * other but will not receive their own speech (echo) if not enabled. + * + * Features of CMX are: + * - Crossconnecting or even conference, if more than two members are together. + * - Force mixing of transmit data with other crossconnect/conference members. + * - Echo generation to benchmark the delay of audio processing. + * - Use hardware to minimize cpu load, disable FIFO load and minimize delay. + * - Dejittering and clock generation. + * + * There are 2 buffers: + * + * + * RX-Buffer + * R W + * | | + * ----------------+-------------+------------------- + * + * The rx-buffer is a ring buffer used to store the received data for each + * individual member. This is only the case if data needs to be dejittered + * or in case of a conference where different clocks require reclocking. + * The transmit-clock (R) will read the buffer. + * If the clock overruns the write-pointer, we will have a buffer underrun. + * If the write pointer always has a certain distance from the transmit- + * clock, we will have a delay. The delay will dynamically be increased and + * reduced. + * + * + * TX-Buffer + * R W + * | | + * -----------------+--------+----------------------- + * + * The tx-buffer is a ring buffer to queue the transmit data from user space + * until it will be mixed or sent. There are two pointers, R and W. If the write + * pointer W would reach or overrun R, the buffer would overrun. In this case + * (some) data is dropped so that it will not overrun. + * Additionally a dynamic dejittering can be enabled. this allows data from + * user space that have jitter and different clock source. + * + * + * Clock: + * + * A Clock is not required, if the data source has exactly one clock. In this + * case the data source is forwarded to the destination. + * + * A Clock is required, because the data source + * - has multiple clocks. + * - has no usable clock due to jitter or packet loss (VoIP). + * In this case the system's clock is used. The clock resolution depends on + * the jiffie resolution. + * + * If a member joins a conference: + * + * - If a member joins, its rx_buff is set to silence and change read pointer + * to transmit clock. + * + * The procedure of received data from card is explained in cmx_receive. + * The procedure of received data from user space is explained in cmx_transmit. + * The procedure of transmit data to card is cmx_send. + * + * + * Interaction with other features: + * + * DTMF: + * DTMF decoding is done before the data is crossconnected. + * + * Volume change: + * Changing rx-volume is done before the data is crossconnected. The tx-volume + * must be changed whenever data is transmitted to the card by the cmx. + * + * Tones: + * If a tone is enabled, it will be processed whenever data is transmitted to + * the card. It will replace the tx-data from the user space. + * If tones are generated by hardware, this conference member is removed for + * this time. + * + * Disable rx-data: + * If cmx is realized in hardware, rx data will be disabled if requested by + * the upper layer. If dtmf decoding is done by software and enabled, rx data + * will not be diabled but blocked to the upper layer. + * + * HFC conference engine: + * If it is possible to realize all features using hardware, hardware will be + * used if not forbidden by control command. Disabling rx-data provides + * absolutely traffic free audio processing. (except for the quick 1-frame + * upload of a tone loop, only once for a new tone) + * + */ + +/* delay.h is required for hw_lock.h */ + +#include +#include +#include +#include "core.h" +#include "dsp.h" +/* + * debugging of multi party conference, + * by using conference even with two members + */ + +/* #define CMX_CONF_DEBUG */ + +/*#define CMX_DEBUG * massive read/write pointer output */ +/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */ + +static inline int +count_list_member(struct list_head *head) +{ + int cnt = 0; + struct list_head *m; + + list_for_each(m, head) + cnt++; + return cnt; +} + +/* + * debug cmx memory structure + */ +void +dsp_cmx_debug(struct dsp *dsp) +{ + struct dsp_conf *conf; + struct dsp_conf_member *member; + struct dsp *odsp; + + printk(KERN_DEBUG "-----Current DSP\n"); + list_for_each_entry(odsp, &dsp_ilist, list) { + printk(KERN_DEBUG "* %s echo=%d txmix=%d", + odsp->name, odsp->echo, odsp->tx_mix); + if (odsp->conf) + printk(" (Conf %d)", odsp->conf->id); + if (dsp == odsp) + printk(" *this*"); + printk("\n"); + } + printk(KERN_DEBUG "-----Current Conf:\n"); + list_for_each_entry(conf, &conf_ilist, list) { + printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf); + list_for_each_entry(member, &conf->mlist, list) { + printk(KERN_DEBUG + " - member = %s (slot_tx %d, bank_tx %d, " + "slot_rx %d, bank_rx %d hfc_conf %d)%s\n", + member->dsp->name, member->dsp->pcm_slot_tx, + member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx, + member->dsp->pcm_bank_rx, member->dsp->hfc_conf, + (member->dsp == dsp) ? " *this*" : ""); + } + } + printk(KERN_DEBUG "-----end\n"); +} + +/* + * search conference + */ +static struct dsp_conf * +dsp_cmx_search_conf(u32 id) +{ + struct dsp_conf *conf; + + if (!id) { + printk(KERN_WARNING "%s: conference ID is 0.\n", __func__); + return NULL; + } + + /* search conference */ + list_for_each_entry(conf, &conf_ilist, list) + if (conf->id == id) + return conf; + + return NULL; +} + + +/* + * add member to conference + */ +static int +dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf) +{ + struct dsp_conf_member *member; + + if (!conf || !dsp) { + printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__); + return -EINVAL; + } + if (dsp->member) { + printk(KERN_WARNING "%s: dsp is already member in a conf.\n", + __func__); + return -EINVAL; + } + + if (dsp->conf) { + printk(KERN_WARNING "%s: dsp is already in a conf.\n", + __func__); + return -EINVAL; + } + + member = kzalloc(sizeof(struct dsp_conf_member), GFP_ATOMIC); + if (!member) { + printk(KERN_ERR "kmalloc struct dsp_conf_member failed\n"); + return -ENOMEM; + } + member->dsp = dsp; + /* clear rx buffer */ + memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); + dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */ + dsp->rx_W = 0; + dsp->rx_R = 0; + + list_add_tail(&member->list, &conf->mlist); + + dsp->conf = conf; + dsp->member = member; + + return 0; +} + + +/* + * del member from conference + */ +int +dsp_cmx_del_conf_member(struct dsp *dsp) +{ + struct dsp_conf_member *member; + + if (!dsp) { + printk(KERN_WARNING "%s: dsp is 0.\n", + __func__); + return -EINVAL; + } + + if (!dsp->conf) { + printk(KERN_WARNING "%s: dsp is not in a conf.\n", + __func__); + return -EINVAL; + } + + if (list_empty(&dsp->conf->mlist)) { + printk(KERN_WARNING "%s: dsp has linked an empty conf.\n", + __func__); + return -EINVAL; + } + + /* find us in conf */ + list_for_each_entry(member, &dsp->conf->mlist, list) { + if (member->dsp == dsp) { + list_del(&member->list); + dsp->conf = NULL; + dsp->member = NULL; + kfree(member); + return 0; + } + } + printk(KERN_WARNING + "%s: dsp is not present in its own conf_meber list.\n", + __func__); + + return -EINVAL; +} + + +/* + * new conference + */ +static struct dsp_conf +*dsp_cmx_new_conf(u32 id) +{ + struct dsp_conf *conf; + + if (!id) { + printk(KERN_WARNING "%s: id is 0.\n", + __func__); + return NULL; + } + + conf = kzalloc(sizeof(struct dsp_conf), GFP_ATOMIC); + if (!conf) { + printk(KERN_ERR "kmalloc struct dsp_conf failed\n"); + return NULL; + } + INIT_LIST_HEAD(&conf->mlist); + conf->id = id; + + list_add_tail(&conf->list, &conf_ilist); + + return conf; +} + + +/* + * del conference + */ +int +dsp_cmx_del_conf(struct dsp_conf *conf) +{ + if (!conf) { + printk(KERN_WARNING "%s: conf is null.\n", + __func__); + return -EINVAL; + } + + if (!list_empty(&conf->mlist)) { + printk(KERN_WARNING "%s: conf not empty.\n", + __func__); + return -EINVAL; + } + list_del(&conf->list); + kfree(conf); + + return 0; +} + + +/* + * send HW message to hfc card + */ +static void +dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2, + u32 param3, u32 param4) +{ + struct mISDN_ctrl_req cq; + + memset(&cq, 0, sizeof(cq)); + cq.op = message; + cq.p1 = param1 | (param2 << 8); + cq.p2 = param3 | (param4 << 8); + if (dsp->ch.peer) + dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq); +} + + +/* + * do hardware update and set the software/hardware flag + * + * either a conference or a dsp instance can be given + * if only dsp instance is given, the instance is not associated with a conf + * and therefore removed. if a conference is given, the dsp is expected to + * be member of that conference. + */ +void +dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp) +{ + struct dsp_conf_member *member, *nextm; + struct dsp *finddsp; + int memb = 0, i, ii, i1, i2; + int freeunits[8]; + u_char freeslots[256]; + int same_hfc = -1, same_pcm = -1, current_conf = -1, + all_conf = 1; + + /* dsp gets updated (no conf) */ + if (!conf) { + if (!dsp) + return; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s checking dsp %s\n", + __func__, dsp->name); +one_member: + /* remove HFC conference if enabled */ + if (dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC conf %d " + "because dsp is split\n", __func__, + dsp->name, dsp->hfc_conf); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT, + 0, 0, 0, 0); + dsp->hfc_conf = -1; + } + /* process hw echo */ + if (dsp->features.pcm_banks < 1) + return; + if (!dsp->echo) { + /* NO ECHO: remove PCM slot if assigned */ + if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing %s from" + " PCM slot %d (TX) %d (RX) because" + " dsp is split (no echo)\n", + __func__, dsp->name, + dsp->pcm_slot_tx, dsp->pcm_slot_rx); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC, + 0, 0, 0, 0); + dsp->pcm_slot_tx = -1; + dsp->pcm_bank_tx = -1; + dsp->pcm_slot_rx = -1; + dsp->pcm_bank_rx = -1; + } + return; + } + /* ECHO: already echo */ + if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 && + dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2) + return; + /* ECHO: if slot already assigned */ + if (dsp->pcm_slot_tx >= 0) { + dsp->pcm_slot_rx = dsp->pcm_slot_tx; + dsp->pcm_bank_tx = 2; /* 2 means loop */ + dsp->pcm_bank_rx = 2; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s refresh %s for echo using slot %d\n", + __func__, dsp->name, + dsp->pcm_slot_tx); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, + dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); + return; + } + /* ECHO: find slot */ + dsp->pcm_slot_tx = -1; + dsp->pcm_slot_rx = -1; + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(finddsp, &dsp_ilist, list) { + if (finddsp->features.pcm_id == dsp->features.pcm_id) { + if (finddsp->pcm_slot_rx >= 0 && + finddsp->pcm_slot_rx < sizeof(freeslots)) + freeslots[finddsp->pcm_slot_tx] = 0; + if (finddsp->pcm_slot_tx >= 0 && + finddsp->pcm_slot_tx < sizeof(freeslots)) + freeslots[finddsp->pcm_slot_rx] = 0; + } + } + i = 0; + ii = dsp->features.pcm_slots; + while (i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available for echo\n", + __func__); + /* no more slots available */ + return; + } + /* assign free slot */ + dsp->pcm_slot_tx = i; + dsp->pcm_slot_rx = i; + dsp->pcm_bank_tx = 2; /* loop */ + dsp->pcm_bank_rx = 2; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s assign echo for %s using slot %d\n", + __func__, dsp->name, dsp->pcm_slot_tx); + dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN, + dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2); + return; + } + + /* conf gets updated (all members) */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s checking conference %d\n", + __func__, conf->id); + + if (list_empty(&conf->mlist)) { + printk(KERN_ERR "%s: conference whithout members\n", + __func__); + return; + } + member = list_entry(conf->mlist.next, struct dsp_conf_member, list); + same_hfc = member->dsp->features.hfc_id; + same_pcm = member->dsp->features.pcm_id; + /* check all members in our conference */ + list_for_each_entry(member, &conf->mlist, list) { + /* check if member uses mixing */ + if (member->dsp->tx_mix) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_mix is turned on\n", __func__, + member->dsp->name); +conf_software: + list_for_each_entry(member, &conf->mlist, list) { + dsp = member->dsp; + /* remove HFC conference if enabled */ + if (dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC " + "conf %d because not " + "possible with hardware\n", + __func__, + dsp->name, + dsp->hfc_conf); + dsp_cmx_hw_message(dsp, + MISDN_CTRL_HFC_CONF_SPLIT, + 0, 0, 0, 0); + dsp->hfc_conf = -1; + } + /* remove PCM slot if assigned */ + if (dsp->pcm_slot_tx >= 0 || + dsp->pcm_slot_rx >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s removing " + "%s from PCM slot %d (TX)" + " slot %d (RX) because not" + " possible with hardware\n", + __func__, + dsp->name, + dsp->pcm_slot_tx, + dsp->pcm_slot_rx); + dsp_cmx_hw_message(dsp, + MISDN_CTRL_HFC_PCM_DISC, + 0, 0, 0, 0); + dsp->pcm_slot_tx = -1; + dsp->pcm_bank_tx = -1; + dsp->pcm_slot_rx = -1; + dsp->pcm_bank_rx = -1; + } + } + conf->hardware = 0; + conf->software = 1; + return; + } + /* check if member has echo turned on */ + if (member->dsp->echo) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "echo is turned on\n", __func__, + member->dsp->name); + goto conf_software; + } + /* check if member has tx_mix turned on */ + if (member->dsp->tx_mix) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_mix is turned on\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if member changes volume at an not suppoted level */ + if (member->dsp->tx_volume) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_volume is changed\n", + __func__, member->dsp->name); + goto conf_software; + } + if (member->dsp->rx_volume) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "rx_volume is changed\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if tx-data turned on */ + if (member->dsp->tx_data) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "tx_data is turned on\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if pipeline exists */ + if (member->dsp->pipeline.inuse) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "pipeline exists\n", __func__, + member->dsp->name); + goto conf_software; + } + /* check if encryption is enabled */ + if (member->dsp->bf_enable) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "%s dsp %s cannot form a " + "conf, because encryption is enabled\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if member is on a card with PCM support */ + if (member->dsp->features.pcm_id < 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "dsp has no PCM bus\n", + __func__, member->dsp->name); + goto conf_software; + } + /* check if relations are on the same PCM bus */ + if (member->dsp->features.pcm_id != same_pcm) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s cannot form a conf, because " + "dsp is on a different PCM bus than the " + "first dsp\n", + __func__, member->dsp->name); + goto conf_software; + } + /* determine if members are on the same hfc chip */ + if (same_hfc != member->dsp->features.hfc_id) + same_hfc = -1; + /* if there are members already in a conference */ + if (current_conf < 0 && member->dsp->hfc_conf >= 0) + current_conf = member->dsp->hfc_conf; + /* if any member is not in a conference */ + if (member->dsp->hfc_conf < 0) + all_conf = 0; + + memb++; + } + + /* if no member, this is an error */ + if (memb < 1) + return; + + /* one member */ + if (memb == 1) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conf %d cannot form a HW conference, " + "because dsp is alone\n", __func__, conf->id); + conf->hardware = 0; + conf->software = 0; + member = list_entry(conf->mlist.next, struct dsp_conf_member, + list); + dsp = member->dsp; + goto one_member; + } + + /* + * ok, now we are sure that all members are on the same pcm. + * now we will see if we have only two members, so we can do + * crossconnections, which don't have any limitations. + */ + + /* if we have only two members */ + if (memb == 2) { + member = list_entry(conf->mlist.next, struct dsp_conf_member, + list); + nextm = list_entry(member->list.next, struct dsp_conf_member, + list); + /* remove HFC conference if enabled */ + if (member->dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC conf %d because " + "two parties require only a PCM slot\n", + __func__, member->dsp->name, + member->dsp->hfc_conf); + dsp_cmx_hw_message(member->dsp, + MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); + member->dsp->hfc_conf = -1; + } + if (nextm->dsp->hfc_conf >= 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s removing %s from HFC conf %d because " + "two parties require only a PCM slot\n", + __func__, nextm->dsp->name, + nextm->dsp->hfc_conf); + dsp_cmx_hw_message(nextm->dsp, + MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0); + nextm->dsp->hfc_conf = -1; + } + /* if members have two banks (and not on the same chip) */ + if (member->dsp->features.pcm_banks > 1 && + nextm->dsp->features.pcm_banks > 1 && + member->dsp->features.hfc_id != + nextm->dsp->features.hfc_id) { + /* if both members have same slots with crossed banks */ + if (member->dsp->pcm_slot_tx >= 0 && + member->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx >= 0 && + nextm->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx == + member->dsp->pcm_slot_rx && + nextm->dsp->pcm_slot_rx == + member->dsp->pcm_slot_tx && + nextm->dsp->pcm_slot_tx == + member->dsp->pcm_slot_tx && + member->dsp->pcm_bank_tx != + member->dsp->pcm_bank_rx && + nextm->dsp->pcm_bank_tx != + nextm->dsp->pcm_bank_rx) { + /* all members have same slot */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s & %s stay joined on " + "PCM slot %d bank %d (TX) bank %d " + "(RX) (on different chips)\n", + __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx, + member->dsp->pcm_bank_tx, + member->dsp->pcm_bank_rx); + conf->hardware = 0; + conf->software = 1; + return; + } + /* find a new slot */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp != member->dsp && + dsp != nextm->dsp && + member->dsp->features.pcm_id == + dsp->features.pcm_id) { + if (dsp->pcm_slot_rx >= 0 && + dsp->pcm_slot_rx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_tx] = 0; + if (dsp->pcm_slot_tx >= 0 && + dsp->pcm_slot_tx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_rx] = 0; + } + } + i = 0; + ii = member->dsp->features.pcm_slots; + while (i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available for " + "%s & %s\n", __func__, + member->dsp->name, + nextm->dsp->name); + /* no more slots available */ + goto conf_software; + } + /* assign free slot */ + member->dsp->pcm_slot_tx = i; + member->dsp->pcm_slot_rx = i; + nextm->dsp->pcm_slot_tx = i; + nextm->dsp->pcm_slot_rx = i; + member->dsp->pcm_bank_rx = 0; + member->dsp->pcm_bank_tx = 1; + nextm->dsp->pcm_bank_rx = 1; + nextm->dsp->pcm_bank_tx = 0; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s adding %s & %s to new PCM slot %d " + "(TX and RX on different chips) because " + "both members have not same slots\n", + __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx); + dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, + member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, + member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); + dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, + nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, + nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); + conf->hardware = 1; + conf->software = 0; + return; + /* if members have one bank (or on the same chip) */ + } else { + /* if both members have different crossed slots */ + if (member->dsp->pcm_slot_tx >= 0 && + member->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx >= 0 && + nextm->dsp->pcm_slot_rx >= 0 && + nextm->dsp->pcm_slot_tx == + member->dsp->pcm_slot_rx && + nextm->dsp->pcm_slot_rx == + member->dsp->pcm_slot_tx && + member->dsp->pcm_slot_tx != + member->dsp->pcm_slot_rx && + member->dsp->pcm_bank_tx == 0 && + member->dsp->pcm_bank_rx == 0 && + nextm->dsp->pcm_bank_tx == 0 && + nextm->dsp->pcm_bank_rx == 0) { + /* all members have same slot */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s dsp %s & %s stay joined on PCM " + "slot %d (TX) %d (RX) on same chip " + "or one bank PCM)\n", __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx, + member->dsp->pcm_slot_rx); + conf->hardware = 0; + conf->software = 1; + return; + } + /* find two new slot */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp != member->dsp && + dsp != nextm->dsp && + member->dsp->features.pcm_id == + dsp->features.pcm_id) { + if (dsp->pcm_slot_rx >= 0 && + dsp->pcm_slot_rx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_tx] = 0; + if (dsp->pcm_slot_tx >= 0 && + dsp->pcm_slot_tx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_rx] = 0; + } + } + i1 = 0; + ii = member->dsp->features.pcm_slots; + while (i1 < ii) { + if (freeslots[i1]) + break; + i1++; + } + if (i1 == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available " + "for %s & %s\n", __func__, + member->dsp->name, + nextm->dsp->name); + /* no more slots available */ + goto conf_software; + } + i2 = i1+1; + while (i2 < ii) { + if (freeslots[i2]) + break; + i2++; + } + if (i2 == ii) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s no slot available " + "for %s & %s\n", + __func__, + member->dsp->name, + nextm->dsp->name); + /* no more slots available */ + goto conf_software; + } + /* assign free slots */ + member->dsp->pcm_slot_tx = i1; + member->dsp->pcm_slot_rx = i2; + nextm->dsp->pcm_slot_tx = i2; + nextm->dsp->pcm_slot_rx = i1; + member->dsp->pcm_bank_rx = 0; + member->dsp->pcm_bank_tx = 0; + nextm->dsp->pcm_bank_rx = 0; + nextm->dsp->pcm_bank_tx = 0; + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s adding %s & %s to new PCM slot %d " + "(TX) %d (RX) on same chip or one bank " + "PCM, because both members have not " + "crossed slots\n", __func__, + member->dsp->name, + nextm->dsp->name, + member->dsp->pcm_slot_tx, + member->dsp->pcm_slot_rx); + dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, + member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx, + member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx); + dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN, + nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx, + nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx); + conf->hardware = 1; + conf->software = 0; + return; + } + } + + /* + * if we have more than two, we may check if we have a conference + * unit available on the chip. also all members must be on the same + */ + + /* if not the same HFC chip */ + if (same_hfc < 0) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conference %d cannot be formed, because " + "members are on different chips or not " + "on HFC chip\n", + __func__, conf->id); + goto conf_software; + } + + /* for more than two members.. */ + + /* in case of hdlc, we change to software */ + if (dsp->hdlc) + goto conf_software; + + /* if all members already have the same conference */ + if (all_conf) + return; + + /* + * if there is an existing conference, but not all members have joined + */ + if (current_conf >= 0) { +join_members: + list_for_each_entry(member, &conf->mlist, list) { + /* join to current conference */ + if (member->dsp->hfc_conf == current_conf) + continue; + /* get a free timeslot first */ + memset(freeslots, 1, sizeof(freeslots)); + list_for_each_entry(dsp, &dsp_ilist, list) { + /* + * not checking current member, because + * slot will be overwritten. + */ + if ( + dsp != member->dsp && + /* dsp must be on the same PCM */ + member->dsp->features.pcm_id == + dsp->features.pcm_id) { + /* dsp must be on a slot */ + if (dsp->pcm_slot_tx >= 0 && + dsp->pcm_slot_tx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_tx] = 0; + if (dsp->pcm_slot_rx >= 0 && + dsp->pcm_slot_rx < + sizeof(freeslots)) + freeslots[dsp->pcm_slot_rx] = 0; + } + } + i = 0; + ii = member->dsp->features.pcm_slots; + while (i < ii) { + if (freeslots[i]) + break; + i++; + } + if (i == ii) { + /* no more slots available */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conference %d cannot be formed," + " because no slot free\n", + __func__, conf->id); + goto conf_software; + } + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s changing dsp %s to HW conference " + "%d slot %d\n", __func__, + member->dsp->name, current_conf, i); + /* assign free slot & set PCM & join conf */ + member->dsp->pcm_slot_tx = i; + member->dsp->pcm_slot_rx = i; + member->dsp->pcm_bank_tx = 2; /* loop */ + member->dsp->pcm_bank_rx = 2; + member->dsp->hfc_conf = current_conf; + dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN, + i, 2, i, 2); + dsp_cmx_hw_message(member->dsp, + MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0); + } + return; + } + + /* + * no member is in a conference yet, so we find a free one + */ + memset(freeunits, 1, sizeof(freeunits)); + list_for_each_entry(dsp, &dsp_ilist, list) { + /* dsp must be on the same chip */ + if (dsp->features.hfc_id == same_hfc && + /* dsp must have joined a HW conference */ + dsp->hfc_conf >= 0 && + /* slot must be within range */ + dsp->hfc_conf < 8) + freeunits[dsp->hfc_conf] = 0; + } + i = 0; + ii = 8; + while (i < ii) { + if (freeunits[i]) + break; + i++; + } + if (i == ii) { + /* no more conferences available */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s conference %d cannot be formed, because " + "no conference number free\n", + __func__, conf->id); + goto conf_software; + } + /* join all members */ + current_conf = i; + goto join_members; +} + + +/* + * conf_id != 0: join or change conference + * conf_id == 0: split from conference if not already + */ +int +dsp_cmx_conf(struct dsp *dsp, u32 conf_id) +{ + int err; + struct dsp_conf *conf; + struct dsp_conf_member *member; + + /* if conference doesn't change */ + if (dsp->conf_id == conf_id) + return 0; + + /* first remove us from current conf */ + if (dsp->conf_id) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "removing us from conference %d\n", + dsp->conf->id); + /* remove us from conf */ + conf = dsp->conf; + err = dsp_cmx_del_conf_member(dsp); + if (err) + return err; + dsp->conf_id = 0; + + /* update hardware */ + dsp_cmx_hardware(NULL, dsp); + + /* conf now empty? */ + if (list_empty(&conf->mlist)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "conference is empty, so we remove it.\n"); + err = dsp_cmx_del_conf(conf); + if (err) + return err; + } else { + /* update members left on conf */ + dsp_cmx_hardware(conf, NULL); + } + } + + /* if split */ + if (!conf_id) + return 0; + + /* now add us to conf */ + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG "searching conference %d\n", + conf_id); + conf = dsp_cmx_search_conf(conf_id); + if (!conf) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "conference doesn't exist yet, creating.\n"); + /* the conference doesn't exist, so we create */ + conf = dsp_cmx_new_conf(conf_id); + if (!conf) + return -EINVAL; + } else if (!list_empty(&conf->mlist)) { + member = list_entry(conf->mlist.next, struct dsp_conf_member, + list); + if (dsp->hdlc && !member->dsp->hdlc) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cannot join transparent conference.\n"); + return -EINVAL; + } + if (!dsp->hdlc && member->dsp->hdlc) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cannot join hdlc conference.\n"); + return -EINVAL; + } + } + /* add conference member */ + err = dsp_cmx_add_conf_member(dsp, conf); + if (err) + return err; + dsp->conf_id = conf_id; + + /* if we are alone, we do nothing! */ + if (list_empty(&conf->mlist)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "we are alone in this conference, so exit.\n"); + /* update hardware */ + dsp_cmx_hardware(NULL, dsp); + return 0; + } + + /* update members on conf */ + dsp_cmx_hardware(conf, NULL); + + return 0; +} + + +/* + * audio data is received from card + */ +void +dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) +{ + u8 *d, *p; + int len = skb->len; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int w, i, ii; + + /* check if we have sompen */ + if (len < 1) + return; + + /* half of the buffer should be larger than maximum packet size */ + if (len >= CMX_BUFF_HALF) { + printk(KERN_ERR + "%s line %d: packet from card is too large (%d bytes). " + "please make card send smaller packets OR increase " + "CMX_BUFF_SIZE\n", __FILE__, __LINE__, len); + return; + } + + /* + * initialize pointers if not already - + * also add delay if requested by PH_SIGNAL + */ + if (dsp->rx_init) { + dsp->rx_init = 0; + if (dsp->features.unordered) { + dsp->rx_R = (hh->id & CMX_BUFF_MASK); + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + } else { + dsp->rx_R = 0; + dsp->rx_W = dsp->cmx_delay; + } + } + /* if frame contains time code, write directly */ + if (dsp->features.unordered) { + dsp->rx_W = (hh->id & CMX_BUFF_MASK); + /* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */ + } + /* + * if we underrun (or maybe overrun), + * we set our new read pointer, and write silence to buffer + */ + if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cmx_receive(dsp=%lx): UNDERRUN (or overrun the " + "maximum delay), adjusting read pointer! " + "(inst %s)\n", (u_long)dsp, dsp->name); + /* flush buffer */ + if (dsp->features.unordered) { + dsp->rx_R = (hh->id & CMX_BUFF_MASK); + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + } else { + dsp->rx_R = 0; + dsp->rx_W = dsp->cmx_delay; + } + memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); + } + /* if we have reached double delay, jump back to middle */ + if (dsp->cmx_delay) + if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >= + (dsp->cmx_delay << 1)) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "cmx_receive(dsp=%lx): OVERRUN (because " + "twice the delay is reached), adjusting " + "read pointer! (inst %s)\n", + (u_long)dsp, dsp->name); + /* flush buffer */ + if (dsp->features.unordered) { + dsp->rx_R = (hh->id & CMX_BUFF_MASK); + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + } else { + dsp->rx_R = 0; + dsp->rx_W = dsp->cmx_delay; + } + memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); + } + + /* show where to write */ +#ifdef CMX_DEBUG + printk(KERN_DEBUG + "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n", + (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name); +#endif + + /* write data into rx_buffer */ + p = skb->data; + d = dsp->rx_buff; + w = dsp->rx_W; + i = 0; + ii = len; + while (i < ii) { + d[w++ & CMX_BUFF_MASK] = *p++; + i++; + } + + /* increase write-pointer */ + dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK); +} + + +/* + * send (mixed) audio data to card and control jitter + */ +static void +dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members) +{ + struct dsp_conf *conf = dsp->conf; + struct dsp *member, *other; + register s32 sample; + u8 *d, *p, *q, *o_q; + struct sk_buff *nskb, *txskb; + int r, rr, t, tt, o_r, o_rr; + int preload = 0; + struct mISDNhead *hh, *thh; + + /* don't process if: */ + if (!dsp->b_active) { /* if not active */ + dsp->last_tx = 0; + return; + } + if (dsp->pcm_slot_tx >= 0 && /* connected to pcm slot */ + dsp->tx_R == dsp->tx_W && /* AND no tx-data */ + !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */ + dsp->last_tx = 0; + return; + } + +#ifdef CMX_DEBUG + printk(KERN_DEBUG + "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n", + members, dsp->name, conf, dsp->rx_R, dsp->rx_W); +#endif + + /* preload if we have delay set */ + if (dsp->cmx_delay && !dsp->last_tx) { + preload = len; + if (preload < 128) + preload = 128; + } + + /* PREPARE RESULT */ + nskb = mI_alloc_skb(len + preload, GFP_ATOMIC); + if (!nskb) { + printk(KERN_ERR + "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", + len + preload); + return; + } + hh = mISDN_HEAD_P(nskb); + hh->prim = PH_DATA_REQ; + hh->id = 0; + dsp->last_tx = 1; + + /* set pointers, indexes and stuff */ + member = dsp; + p = dsp->tx_buff; /* transmit data */ + q = dsp->rx_buff; /* received data */ + d = skb_put(nskb, preload + len); /* result */ + t = dsp->tx_R; /* tx-pointers */ + tt = dsp->tx_W; + r = dsp->rx_R; /* rx-pointers */ + rr = (r + len) & CMX_BUFF_MASK; + + /* preload with silence, if required */ + if (preload) { + memset(d, dsp_silence, preload); + d += preload; + } + + /* PROCESS TONES/TX-DATA ONLY */ + if (dsp->tone.tone && dsp->tone.software) { + /* -> copy tone */ + dsp_tone_copy(dsp, d, len); + dsp->tx_R = 0; /* clear tx buffer */ + dsp->tx_W = 0; + goto send_packet; + } + /* if we have tx-data but do not use mixing */ + if (!dsp->tx_mix && t != tt) { + /* -> send tx-data and continue when not enough */ +#ifdef CMX_TX_DEBUG + sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p); +#endif + while (r != rr && t != tt) { +#ifdef CMX_TX_DEBUG + if (strlen(debugbuf) < 48) + sprintf(debugbuf+strlen(debugbuf), " %02x", p[t]); +#endif + *d++ = p[t]; /* write tx_buff */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + if (r == rr) { + dsp->tx_R = t; +#ifdef CMX_TX_DEBUG + printk(KERN_DEBUG "%s\n", debugbuf); +#endif + goto send_packet; + } + } +#ifdef CMX_TX_DEBUG + printk(KERN_DEBUG "%s\n", debugbuf); +#endif + + /* PROCESS DATA (one member / no conf) */ + if (!conf || members <= 1) { + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* -> send tx-data if available or use 0-volume */ + while (r != rr && t != tt) { + *d++ = p[t]; /* write tx_buff */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + if (r != rr) + memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK); + /* -> if echo is enabled */ + } else { + /* + * -> mix tx-data with echo if available, + * or use echo only + */ + while (r != rr && t != tt) { + *d++ = dsp_audio_mix_law[(p[t]<<8)|q[r]]; + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + while (r != rr) { + *d++ = q[r]; /* echo */ + r = (r+1) & CMX_BUFF_MASK; + } + } + dsp->tx_R = t; + goto send_packet; + } + /* PROCESS DATA (two members) */ +#ifdef CMX_CONF_DEBUG + if (0) { +#else + if (members == 2) { +#endif + /* "other" becomes other party */ + other = (list_entry(conf->mlist.next, + struct dsp_conf_member, list))->dsp; + if (other == member) + other = (list_entry(conf->mlist.prev, + struct dsp_conf_member, list))->dsp; + o_q = other->rx_buff; /* received data */ + o_rr = (other->rx_R + len) & CMX_BUFF_MASK; + /* end of rx-pointer */ + o_r = (o_rr - rr + r) & CMX_BUFF_MASK; + /* start rx-pointer at current read position*/ + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* + * -> copy other member's rx-data, + * if tx-data is available, mix + */ + while (o_r != o_rr && t != tt) { + *d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]]; + t = (t+1) & CMX_BUFF_MASK; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + while (o_r != o_rr) { + *d++ = o_q[o_r]; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + /* -> if echo is enabled */ + } else { + /* + * -> mix other member's rx-data with echo, + * if tx-data is available, mix + */ + while (r != rr && t != tt) { + sample = dsp_audio_law_to_s32[p[t]] + + dsp_audio_law_to_s32[q[r]] + + dsp_audio_law_to_s32[o_q[o_r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* tx-data + rx_data + echo */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + while (r != rr) { + *d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]]; + r = (r+1) & CMX_BUFF_MASK; + o_r = (o_r+1) & CMX_BUFF_MASK; + } + } + dsp->tx_R = t; + goto send_packet; + } +#ifdef DSP_NEVER_DEFINED + } +#endif + /* PROCESS DATA (three or more members) */ + /* -> if echo is NOT enabled */ + if (!dsp->echo) { + /* + * -> substract rx-data from conf-data, + * if tx-data is available, mix + */ + while (r != rr && t != tt) { + sample = dsp_audio_law_to_s32[p[t]] + *c++ - + dsp_audio_law_to_s32[q[r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf-rx+tx */ + r = (r+1) & CMX_BUFF_MASK; + t = (t+1) & CMX_BUFF_MASK; + } + while (r != rr) { + sample = *c++ - dsp_audio_law_to_s32[q[r]]; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf-rx */ + r = (r+1) & CMX_BUFF_MASK; + } + /* -> if echo is enabled */ + } else { + /* + * -> encode conf-data, if tx-data + * is available, mix + */ + while (r != rr && t != tt) { + sample = dsp_audio_law_to_s32[p[t]] + *c++; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf(echo)+tx */ + t = (t+1) & CMX_BUFF_MASK; + r = (r+1) & CMX_BUFF_MASK; + } + while (r != rr) { + sample = *c++; + if (sample < -32768) + sample = -32768; + else if (sample > 32767) + sample = 32767; + *d++ = dsp_audio_s16_to_law[sample & 0xffff]; + /* conf(echo) */ + r = (r+1) & CMX_BUFF_MASK; + } + } + dsp->tx_R = t; + goto send_packet; + +send_packet: + /* + * send tx-data if enabled - don't filter, + * becuase we want what we send, not what we filtered + */ + if (dsp->tx_data) { + /* PREPARE RESULT */ + txskb = mI_alloc_skb(len, GFP_ATOMIC); + if (!txskb) { + printk(KERN_ERR + "FATAL ERROR in mISDN_dsp.o: " + "cannot alloc %d bytes\n", len); + } else { + thh = mISDN_HEAD_P(txskb); + thh->prim = DL_DATA_REQ; + thh->id = 0; + memcpy(skb_put(txskb, len), nskb->data+preload, len); + /* queue (trigger later) */ + skb_queue_tail(&dsp->sendq, txskb); + } + } + /* adjust volume */ + if (dsp->tx_volume) + dsp_change_volume(nskb, dsp->tx_volume); + /* pipeline */ + if (dsp->pipeline.inuse) + dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, nskb->len); + /* crypt */ + if (dsp->bf_enable) + dsp_bf_encrypt(dsp, nskb->data, nskb->len); + /* queue and trigger */ + skb_queue_tail(&dsp->sendq, nskb); + schedule_work(&dsp->workq); +} + +u32 samplecount; +struct timer_list dsp_spl_tl; +u32 dsp_spl_jiffies; /* calculate the next time to fire */ +u32 dsp_start_jiffies; /* jiffies at the time, the calculation begins */ +struct timeval dsp_start_tv; /* time at start of calculation */ + +void +dsp_cmx_send(void *arg) +{ + struct dsp_conf *conf; + struct dsp_conf_member *member; + struct dsp *dsp; + int mustmix, members; + s32 mixbuffer[MAX_POLL+100], *c; + u8 *p, *q; + int r, rr; + int jittercheck = 0, delay, i; + u_long flags; + struct timeval tv; + u32 elapsed; + s16 length; + + /* lock */ + spin_lock_irqsave(&dsp_lock, flags); + + if (!dsp_start_tv.tv_sec) { + do_gettimeofday(&dsp_start_tv); + length = dsp_poll; + } else { + do_gettimeofday(&tv); + elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000) + + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125)); + dsp_start_tv.tv_sec = tv.tv_sec; + dsp_start_tv.tv_usec = tv.tv_usec; + length = elapsed; + } + if (length > MAX_POLL + 100) + length = MAX_POLL + 100; +/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n", + length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16, + dsp_poll_diff & 0xffff); + */ + + /* + * check if jitter needs to be checked + * (this is about every second = 8192 samples) + */ + samplecount += length; + if ((samplecount & 8191) < length) + jittercheck = 1; + + /* loop all members that do not require conference mixing */ + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp->hdlc) + continue; + conf = dsp->conf; + mustmix = 0; + members = 0; + if (conf) { + members = count_list_member(&conf->mlist); +#ifdef CMX_CONF_DEBUG + if (conf->software && members > 1) +#else + if (conf->software && members > 2) +#endif + mustmix = 1; + } + + /* transmission required */ + if (!mustmix) { + dsp_cmx_send_member(dsp, length, mixbuffer, members); + + /* + * unused mixbuffer is given to prevent a + * potential null-pointer-bug + */ + } + } + + /* loop all members that require conference mixing */ + list_for_each_entry(conf, &conf_ilist, list) { + /* count members and check hardware */ + members = count_list_member(&conf->mlist); +#ifdef CMX_CONF_DEBUG + if (conf->software && members > 1) { +#else + if (conf->software && members > 2) { +#endif + /* check for hdlc conf */ + member = list_entry(conf->mlist.next, + struct dsp_conf_member, list); + if (member->dsp->hdlc) + continue; + /* mix all data */ + memset(mixbuffer, 0, length*sizeof(s32)); + list_for_each_entry(member, &conf->mlist, list) { + dsp = member->dsp; + /* get range of data to mix */ + c = mixbuffer; + q = dsp->rx_buff; + r = dsp->rx_R; + rr = (r + length) & CMX_BUFF_MASK; + /* add member's data */ + while (r != rr) { + *c++ += dsp_audio_law_to_s32[q[r]]; + r = (r+1) & CMX_BUFF_MASK; + } + } + + /* process each member */ + list_for_each_entry(member, &conf->mlist, list) { + /* transmission */ + dsp_cmx_send_member(member->dsp, length, + mixbuffer, members); + } + } + } + + /* delete rx-data, increment buffers, change pointers */ + list_for_each_entry(dsp, &dsp_ilist, list) { + if (dsp->hdlc) + continue; + p = dsp->rx_buff; + q = dsp->tx_buff; + r = dsp->rx_R; + /* move receive pointer when receiving */ + if (!dsp->rx_is_off) { + rr = (r + length) & CMX_BUFF_MASK; + /* delete rx-data */ + while (r != rr) { + p[r] = dsp_silence; + r = (r+1) & CMX_BUFF_MASK; + } + /* increment rx-buffer pointer */ + dsp->rx_R = r; /* write incremented read pointer */ + } + + /* check current rx_delay */ + delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK; + if (delay >= CMX_BUFF_HALF) + delay = 0; /* will be the delay before next write */ + /* check for lower delay */ + if (delay < dsp->rx_delay[0]) + dsp->rx_delay[0] = delay; + /* check current tx_delay */ + delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK; + if (delay >= CMX_BUFF_HALF) + delay = 0; /* will be the delay before next write */ + /* check for lower delay */ + if (delay < dsp->tx_delay[0]) + dsp->tx_delay[0] = delay; + if (jittercheck) { + /* find the lowest of all rx_delays */ + delay = dsp->rx_delay[0]; + i = 1; + while (i < MAX_SECONDS_JITTER_CHECK) { + if (delay > dsp->rx_delay[i]) + delay = dsp->rx_delay[i]; + i++; + } + /* + * remove rx_delay only if we have delay AND we + * have not preset cmx_delay + */ + if (delay && !dsp->cmx_delay) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s lowest rx_delay of %d bytes for" + " dsp %s are now removed.\n", + __func__, delay, + dsp->name); + r = dsp->rx_R; + rr = (r + delay) & CMX_BUFF_MASK; + /* delete rx-data */ + while (r != rr) { + p[r] = dsp_silence; + r = (r+1) & CMX_BUFF_MASK; + } + /* increment rx-buffer pointer */ + dsp->rx_R = r; + /* write incremented read pointer */ + } + /* find the lowest of all tx_delays */ + delay = dsp->tx_delay[0]; + i = 1; + while (i < MAX_SECONDS_JITTER_CHECK) { + if (delay > dsp->tx_delay[i]) + delay = dsp->tx_delay[i]; + i++; + } + /* + * remove delay only if we have delay AND we + * have enabled tx_dejitter + */ + if (delay && dsp->tx_dejitter) { + if (dsp_debug & DEBUG_DSP_CMX) + printk(KERN_DEBUG + "%s lowest tx_delay of %d bytes for" + " dsp %s are now removed.\n", + __func__, delay, + dsp->name); + r = dsp->tx_R; + rr = (r + delay) & CMX_BUFF_MASK; + /* delete tx-data */ + while (r != rr) { + q[r] = dsp_silence; + r = (r+1) & CMX_BUFF_MASK; + } + /* increment rx-buffer pointer */ + dsp->tx_R = r; + /* write incremented read pointer */ + } + /* scroll up delays */ + i = MAX_SECONDS_JITTER_CHECK - 1; + while (i) { + dsp->rx_delay[i] = dsp->rx_delay[i-1]; + dsp->tx_delay[i] = dsp->tx_delay[i-1]; + i--; + } + dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ + dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */ + } + } + + /* if next event would be in the past ... */ + if ((s32)(dsp_spl_jiffies+dsp_tics-jiffies) <= 0) + dsp_spl_jiffies = jiffies + 1; + else + dsp_spl_jiffies += dsp_tics; + + dsp_spl_tl.expires = dsp_spl_jiffies; + add_timer(&dsp_spl_tl); + + /* unlock */ + spin_unlock_irqrestore(&dsp_lock, flags); +} + +/* + * audio data is transmitted from upper layer to the dsp + */ +void +dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) +{ + u_int w, ww; + u8 *d, *p; + int space; /* todo: , l = skb->len; */ +#ifdef CMX_TX_DEBUG + char debugbuf[256] = ""; +#endif + + /* check if there is enough space, and then copy */ + w = dsp->tx_W; + ww = dsp->tx_R; + p = dsp->tx_buff; + d = skb->data; + space = ww-w; + if (space <= 0) + space += CMX_BUFF_SIZE; + /* write-pointer should not overrun nor reach read pointer */ + if (space-1 < skb->len) + /* write to the space we have left */ + ww = (ww - 1) & CMX_BUFF_MASK; + else + /* write until all byte are copied */ + ww = (w + skb->len) & CMX_BUFF_MASK; + dsp->tx_W = ww; + + /* show current buffer */ +#ifdef CMX_DEBUG + printk(KERN_DEBUG + "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", + (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->name); +#endif + + /* copy transmit data to tx-buffer */ +#ifdef CMX_TX_DEBUG + sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p); +#endif + while (w != ww) { +#ifdef CMX_TX_DEBUG + if (strlen(debugbuf) < 48) + sprintf(debugbuf+strlen(debugbuf), " %02x", *d); +#endif + p[w] = *d++; + w = (w+1) & CMX_BUFF_MASK; + } +#ifdef CMX_TX_DEBUG + printk(KERN_DEBUG "%s\n", debugbuf); +#endif + +} + +/* + * hdlc data is received from card and sent to all members. + */ +void +dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb) +{ + struct sk_buff *nskb = NULL; + struct dsp_conf_member *member; + struct mISDNhead *hh; + + /* not if not active */ + if (!dsp->b_active) + return; + + /* check if we have sompen */ + if (skb->len < 1) + return; + + /* no conf */ + if (!dsp->conf) { + /* in case of hardware (echo) */ + if (dsp->pcm_slot_tx >= 0) + return; + if (dsp->echo) + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + hh = mISDN_HEAD_P(nskb); + hh->prim = PH_DATA_REQ; + hh->id = 0; + skb_queue_tail(&dsp->sendq, nskb); + schedule_work(&dsp->workq); + } + return; + } + /* in case of hardware conference */ + if (dsp->conf->hardware) + return; + list_for_each_entry(member, &dsp->conf->mlist, list) { + if (dsp->echo || member->dsp != dsp) { + nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + hh = mISDN_HEAD_P(nskb); + hh->prim = PH_DATA_REQ; + hh->id = 0; + skb_queue_tail(&member->dsp->sendq, nskb); + schedule_work(&member->dsp->workq); + } + } + } +} + + diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c new file mode 100644 index 000000000000..2f10ed82c0db --- /dev/null +++ b/drivers/isdn/mISDN/dsp_core.c @@ -0,0 +1,1191 @@ +/* + * Author Andreas Eversberg (jolly@eversberg.eu) + * Based on source code structure by + * Karsten Keil (keil@isdn4linux.de) + * + * This file is (c) under GNU PUBLIC LICENSE + * For changes and modifications please read + * ../../../Documentation/isdn/mISDN.cert + * + * Thanks to Karsten Keil (great drivers) + * Cologne Chip (great chips) + * + * This module does: + * Real-time tone generation + * DTMF detection + * Real-time cross-connection and conferrence + * Compensate jitter due to system load and hardware fault. + * All features are done in kernel space and will be realized + * using hardware, if available and supported by chip set. + * Blowfish encryption/decryption + */ + +/* STRUCTURE: + * + * The dsp module provides layer 2 for b-channels (64kbit). It provides + * transparent audio forwarding with special digital signal processing: + * + * - (1) generation of tones + * - (2) detection of dtmf tones + * - (3) crossconnecting and conferences (clocking) + * - (4) echo generation for delay test + * - (5) volume control + * - (6) disable receive data + * - (7) pipeline + * - (8) encryption/decryption + * + * Look: + * TX RX + * ------upper layer------ + * | ^ + * | |(6) + * v | + * +-----+-------------+-----+ + * |(3)(4) | + * | CMX | + * | | + * | +-------------+ + * | | ^ + * | | | + * |+---------+| +----+----+ + * ||(1) || |(2) | + * || || | | + * || Tones || | DTMF | + * || || | | + * || || | | + * |+----+----+| +----+----+ + * +-----+-----+ ^ + * | | + * v | + * +----+----+ +----+----+ + * |(5) | |(5) | + * | | | | + * |TX Volume| |RX Volume| + * | | | | + * | | | | + * +----+----+ +----+----+ + * | ^ + * | | + * v | + * +----+-------------+----+ + * |(7) | + * | | + * | Pipeline Processing | + * | | + * | | + * +----+-------------+----+ + * | ^ + * | | + * v | + * +----+----+ +----+----+ + * |(8) | |(8) | + * | | | | + * | Encrypt | | Decrypt | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | ^ + * | | + * v | + * ------card layer------ + * TX RX + * + * Above you can see the logical data flow. If software is used to do the + * process, it is actually the real data flow. If hardware is used, data + * may not flow, but hardware commands to the card, to provide the data flow + * as shown. + * + * NOTE: The channel must be activated in order to make dsp work, even if + * no data flow to the upper layer is intended. Activation can be done + * after and before controlling the setting using PH_CONTROL requests. + * + * DTMF: Will be detected by hardware if possible. It is done before CMX + * processing. + * + * Tones: Will be generated via software if endless looped audio fifos are + * not supported by hardware. Tones will override all data from CMX. + * It is not required to join a conference to use tones at any time. + * + * CMX: Is transparent when not used. When it is used, it will do + * crossconnections and conferences via software if not possible through + * hardware. If hardware capability is available, hardware is used. + * + * Echo: Is generated by CMX and is used to check performane of hard and + * software CMX. + * + * The CMX has special functions for conferences with one, two and more + * members. It will allow different types of data flow. Receive and transmit + * data to/form upper layer may be swithed on/off individually without loosing + * features of CMX, Tones and DTMF. + * + * Echo Cancellation: Sometimes we like to cancel echo from the interface. + * Note that a VoIP call may not have echo caused by the IP phone. The echo + * is generated by the telephone line connected to it. Because the delay + * is high, it becomes an echo. RESULT: Echo Cachelation is required if + * both echo AND delay is applied to an interface. + * Remember that software CMX always generates a more or less delay. + * + * If all used features can be realized in hardware, and if transmit and/or + * receive data ist disabled, the card may not send/receive any data at all. + * Not receiving is usefull if only announcements are played. Not sending is + * usefull if an answering machine records audio. Not sending and receiving is + * usefull during most states of the call. If supported by hardware, tones + * will be played without cpu load. Small PBXs and NT-Mode applications will + * not need expensive hardware when processing calls. + * + * + * LOCKING: + * + * When data is received from upper or lower layer (card), the complete dsp + * module is locked by a global lock. This lock MUST lock irq, because it + * must lock timer events by DSP poll timer. + * When data is ready to be transmitted down, the data is queued and sent + * outside lock and timer event. + * PH_CONTROL must not change any settings, join or split conference members + * during process of data. + * + * HDLC: + * + * It works quite the same as transparent, except that HDLC data is forwarded + * to all other conference members if no hardware bridging is possible. + * Send data will be writte to sendq. Sendq will be sent if confirm is received. + * Conference cannot join, if one member is not hdlc. + * + */ + +#include +#include +#include +#include +#include +#include "core.h" +#include "dsp.h" + +const char *mISDN_dsp_revision = "2.0"; + +static int debug; +static int options; +static int poll; +static int dtmfthreshold = 100; + +MODULE_AUTHOR("Andreas Eversberg"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(options, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR); +MODULE_LICENSE("GPL"); + +/*int spinnest = 0;*/ + +spinlock_t dsp_lock; /* global dsp lock */ +struct list_head dsp_ilist; +struct list_head conf_ilist; +int dsp_debug; +int dsp_options; +int dsp_poll, dsp_tics; + +/* check if rx may be turned off or must be turned on */ +static void +dsp_rx_off_member(struct dsp *dsp) +{ + struct mISDN_ctrl_req cq; + int rx_off = 1; + + if (!dsp->features_rx_off) + return; + + /* not disabled */ + if (!dsp->rx_disabled) + rx_off = 0; + /* software dtmf */ + else if (dsp->dtmf.software) + rx_off = 0; + /* echo in software */ + else if (dsp->echo && dsp->pcm_slot_tx < 0) + rx_off = 0; + /* bridge in software */ + else if (dsp->conf) { + if (dsp->conf->software) + rx_off = 0; + } + + if (rx_off == dsp->rx_is_off) + return; + + if (!dsp->ch.peer) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: no peer, no rx_off\n", + __func__); + return; + } + cq.op = MISDN_CTRL_RX_OFF; + cq.p1 = rx_off; + if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", + __func__); + return; + } + dsp->rx_is_off = rx_off; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: %s set rx_off = %d\n", + __func__, dsp->name, rx_off); +} +static void +dsp_rx_off(struct dsp *dsp) +{ + struct dsp_conf_member *member; + + if (dsp_options & DSP_OPT_NOHARDWARE) + return; + + /* no conf */ + if (!dsp->conf) { + dsp_rx_off_member(dsp); + return; + } + /* check all members in conf */ + list_for_each_entry(member, &dsp->conf->mlist, list) { + dsp_rx_off_member(member->dsp); + } +} + +static int +dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) +{ + struct sk_buff *nskb; + int ret = 0; + int cont; + u8 *data; + int len; + + if (skb->len < sizeof(int)) + printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__); + cont = *((int *)skb->data); + len = skb->len - sizeof(int); + data = skb->data + sizeof(int); + + switch (cont) { + case DTMF_TONE_START: /* turn on DTMF */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: start dtmf\n", __func__); + if (len == sizeof(int)) { + printk(KERN_NOTICE "changing DTMF Threshold " + "to %d\n", *((int *)data)); + dsp->dtmf.treshold = (*(int *)data) * 10000; + } + /* init goertzel */ + dsp_dtmf_goertzel_init(dsp); + + /* check dtmf hardware */ + dsp_dtmf_hardware(dsp); + break; + case DTMF_TONE_STOP: /* turn off DTMF */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: stop dtmf\n", __func__); + dsp->dtmf.hardware = 0; + dsp->dtmf.software = 0; + break; + case DSP_CONF_JOIN: /* join / update conference */ + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + if (*((u32 *)data) == 0) + goto conf_split; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: join conference %d\n", + __func__, *((u32 *)data)); + ret = dsp_cmx_conf(dsp, *((u32 *)data)); + /* dsp_cmx_hardware will also be called here */ + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_CONF_SPLIT: /* remove from conference */ +conf_split: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: release conference\n", __func__); + ret = dsp_cmx_conf(dsp, 0); + /* dsp_cmx_hardware will also be called here */ + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + dsp_rx_off(dsp); + break; + case DSP_TONE_PATT_ON: /* play tone */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn tone 0x%x on\n", + __func__, *((int *)skb->data)); + ret = dsp_tone(dsp, *((int *)data)); + if (!ret) { + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + } + if (!dsp->tone.tone) + goto tone_off; + break; + case DSP_TONE_PATT_OFF: /* stop tone */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn tone off\n", __func__); + dsp_tone(dsp, 0); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + /* reset tx buffers (user space data) */ +tone_off: + dsp->rx_W = 0; + dsp->rx_R = 0; + break; + case DSP_VOL_CHANGE_TX: /* change volume */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->tx_volume = *((int *)data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change tx vol to %d\n", + __func__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + break; + case DSP_VOL_CHANGE_RX: /* change volume */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->rx_volume = *((int *)data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change rx vol to %d\n", + __func__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + break; + case DSP_ECHO_ON: /* enable echo */ + dsp->echo = 1; /* soft echo */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_ECHO_OFF: /* disable echo */ + dsp->echo = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_RECEIVE_ON: /* enable receive to user space */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable receive to user " + "space\n", __func__); + dsp->rx_disabled = 0; + dsp_rx_off(dsp); + break; + case DSP_RECEIVE_OFF: /* disable receive to user space */ + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable receive to " + "user space\n", __func__); + dsp->rx_disabled = 1; + dsp_rx_off(dsp); + break; + case DSP_MIX_ON: /* enable mixing of tx data */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable mixing of " + "tx-data with conf mebers\n", __func__); + dsp->tx_mix = 1; + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_MIX_OFF: /* disable mixing of tx data */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable mixing of " + "tx-data with conf mebers\n", __func__); + dsp->tx_mix = 0; + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_TXDATA_ON: /* enable txdata */ + dsp->tx_data = 1; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: enable tx-data\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_TXDATA_OFF: /* disable txdata */ + dsp->tx_data = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: disable tx-data\n", __func__); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + if (dsp_debug & DEBUG_DSP_CMX) + dsp_cmx_debug(dsp); + break; + case DSP_DELAY: /* use delay algorithm instead of dynamic + jitter algorithm */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < sizeof(int)) { + ret = -EINVAL; + break; + } + dsp->cmx_delay = (*((int *)data)) << 3; + /* miliseconds to samples */ + if (dsp->cmx_delay >= (CMX_BUFF_HALF>>1)) + /* clip to half of maximum usable buffer + (half of half buffer) */ + dsp->cmx_delay = (CMX_BUFF_HALF>>1) - 1; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use delay algorithm to " + "compensate jitter (%d samples)\n", + __func__, dsp->cmx_delay); + break; + case DSP_JITTER: /* use dynamic jitter algorithm instead of + delay algorithm */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + dsp->cmx_delay = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use jitter algorithm to " + "compensate jitter\n", __func__); + break; + case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + dsp->tx_dejitter = 1; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use dejitter on TX " + "buffer\n", __func__); + break; + case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + dsp->tx_dejitter = 0; + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: use TX buffer without " + "dejittering\n", __func__); + break; + case DSP_PIPELINE_CFG: + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len > 0 && ((char *)data)[len - 1]) { + printk(KERN_DEBUG "%s: pipeline config string " + "is not NULL terminated!\n", __func__); + ret = -EINVAL; + } else { + dsp->pipeline.inuse = 1; + dsp_cmx_hardware(dsp->conf, dsp); + ret = dsp_pipeline_build(&dsp->pipeline, + len > 0 ? (char *)data : NULL); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + } + break; + case DSP_BF_ENABLE_KEY: /* turn blowfish on */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (len < 4 || len > 56) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn blowfish on (key " + "not shown)\n", __func__); + ret = dsp_bf_init(dsp, (u8 *)data, len); + /* set new cont */ + if (!ret) + cont = DSP_BF_ACCEPT; + else + cont = DSP_BF_REJECT; + /* send indication if it worked to set it */ + nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY, + sizeof(int), &cont, GFP_ATOMIC); + if (nskb) { + if (dsp->up) { + if (dsp->up->send(dsp->up, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } + if (!ret) { + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + } + break; + case DSP_BF_DISABLE: /* turn blowfish off */ + if (dsp->hdlc) { + ret = -EINVAL; + break; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: turn blowfish off\n", __func__); + dsp_bf_cleanup(dsp); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", + __func__, cont); + ret = -EINVAL; + } + return ret; +} + +static void +get_features(struct mISDNchannel *ch) +{ + struct dsp *dsp = container_of(ch, struct dsp, ch); + struct mISDN_ctrl_req cq; + + if (dsp_options & DSP_OPT_NOHARDWARE) + return; + if (!ch->peer) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: no peer, no features\n", + __func__); + return; + } + memset(&cq, 0, sizeof(cq)); + cq.op = MISDN_CTRL_GETOP; + if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } + if (cq.op & MISDN_CTRL_RX_OFF) + dsp->features_rx_off = 1; + if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) { + cq.op = MISDN_CTRL_HW_FEATURES; + *((u_long *)&cq.p1) = (u_long)&dsp->features; + if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n", + __func__); + } + } else + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: features not supported for %s\n", + __func__, dsp->name); +} + +static int +dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct dsp *dsp = container_of(ch, struct dsp, ch); + struct mISDNhead *hh; + int ret = 0; + u8 *digits; + int cont; + struct sk_buff *nskb; + u_long flags; + + hh = mISDN_HEAD_P(skb); + switch (hh->prim) { + /* FROM DOWN */ + case (PH_DATA_CNF): + dsp->data_pending = 0; + /* trigger next hdlc frame, if any */ + if (dsp->hdlc) { + spin_lock_irqsave(&dsp_lock, flags); + if (dsp->b_active) + schedule_work(&dsp->workq); + spin_unlock_irqrestore(&dsp_lock, flags); + } + break; + case (PH_DATA_IND): + case (DL_DATA_IND): + if (skb->len < 1) { + ret = -EINVAL; + break; + } + if (dsp->rx_is_off) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: rx-data during rx_off" + " for %s\n", + __func__, dsp->name); + } + if (dsp->hdlc) { + /* hdlc */ + spin_lock_irqsave(&dsp_lock, flags); + dsp_cmx_hdlc(dsp, skb); + spin_unlock_irqrestore(&dsp_lock, flags); + if (dsp->rx_disabled) { + /* if receive is not allowed */ + break; + } + hh->prim = DL_DATA_IND; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + } + + /* decrypt if enabled */ + if (dsp->bf_enable) + dsp_bf_decrypt(dsp, skb->data, skb->len); + /* pipeline */ + if (dsp->pipeline.inuse) + dsp_pipeline_process_rx(&dsp->pipeline, skb->data, + skb->len); + /* change volume if requested */ + if (dsp->rx_volume) + dsp_change_volume(skb, dsp->rx_volume); + + /* check if dtmf soft decoding is turned on */ + if (dsp->dtmf.software) { + digits = dsp_dtmf_goertzel_decode(dsp, skb->data, + skb->len, (dsp_options&DSP_OPT_ULAW)?1:0); + while (*digits) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s: digit" + "(%c) to layer %s\n", + __func__, *digits, dsp->name); + cont = DTMF_TONE_VAL | *digits; + nskb = _alloc_mISDN_skb(PH_CONTROL_IND, + MISDN_ID_ANY, sizeof(int), &cont, + GFP_ATOMIC); + if (nskb) { + if (dsp->up) { + if (dsp->up->send( + dsp->up, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } + digits++; + } + } + /* we need to process receive data if software */ + spin_lock_irqsave(&dsp_lock, flags); + if (dsp->pcm_slot_tx < 0 && dsp->pcm_slot_rx < 0) { + /* process data from card at cmx */ + dsp_cmx_receive(dsp, skb); + } + spin_unlock_irqrestore(&dsp_lock, flags); + + if (dsp->rx_disabled) { + /* if receive is not allowed */ + break; + } + hh->prim = DL_DATA_IND; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + case (PH_CONTROL_IND): + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "%s: PH_CONTROL INDICATION " + "received: %x (len %d) %s\n", __func__, + hh->id, skb->len, dsp->name); + switch (hh->id) { + case (DTMF_HFC_COEF): /* getting coefficients */ + if (!dsp->dtmf.hardware) { + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "%s: ignoring DTMF " + "coefficients from HFC\n", + __func__); + break; + } + digits = dsp_dtmf_goertzel_decode(dsp, skb->data, + skb->len, 2); + while (*digits) { + int k; + struct sk_buff *nskb; + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s: digit" + "(%c) to layer %s\n", + __func__, *digits, dsp->name); + k = *digits | DTMF_TONE_VAL; + nskb = _alloc_mISDN_skb(PH_CONTROL_IND, + MISDN_ID_ANY, sizeof(int), &k, + GFP_ATOMIC); + if (nskb) { + if (dsp->up) { + if (dsp->up->send( + dsp->up, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } + digits++; + } + break; + case (HFC_VOL_CHANGE_TX): /* change volume */ + if (skb->len != sizeof(int)) { + ret = -EINVAL; + break; + } + spin_lock_irqsave(&dsp_lock, flags); + dsp->tx_volume = *((int *)skb->data); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: change tx volume to " + "%d\n", __func__, dsp->tx_volume); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + spin_unlock_irqrestore(&dsp_lock, flags); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: ctrl ind %x unhandled " + "%s\n", __func__, hh->id, dsp->name); + ret = -EINVAL; + } + break; + case (PH_ACTIVATE_IND): + case (PH_ACTIVATE_CNF): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: b_channel is now active %s\n", + __func__, dsp->name); + /* bchannel now active */ + spin_lock_irqsave(&dsp_lock, flags); + dsp->b_active = 1; + dsp->data_pending = 0; + dsp->rx_init = 1; + /* rx_W and rx_R will be adjusted on first frame */ + dsp->rx_W = 0; + dsp->rx_R = 0; + memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff)); + dsp_cmx_hardware(dsp->conf, dsp); + dsp_dtmf_hardware(dsp); + dsp_rx_off(dsp); + spin_unlock_irqrestore(&dsp_lock, flags); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: done with activation, sending " + "confirm to user space. %s\n", __func__, + dsp->name); + /* send activation to upper layer */ + hh->prim = DL_ESTABLISH_CNF; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + case (PH_DEACTIVATE_IND): + case (PH_DEACTIVATE_CNF): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: b_channel is now inactive %s\n", + __func__, dsp->name); + /* bchannel now inactive */ + spin_lock_irqsave(&dsp_lock, flags); + dsp->b_active = 0; + dsp->data_pending = 0; + dsp_cmx_hardware(dsp->conf, dsp); + dsp_rx_off(dsp); + spin_unlock_irqrestore(&dsp_lock, flags); + hh->prim = DL_RELEASE_CNF; + if (dsp->up) + return dsp->up->send(dsp->up, skb); + break; + /* FROM UP */ + case (DL_DATA_REQ): + case (PH_DATA_REQ): + if (skb->len < 1) { + ret = -EINVAL; + break; + } + if (dsp->hdlc) { + /* hdlc */ + spin_lock_irqsave(&dsp_lock, flags); + if (dsp->b_active) { + skb_queue_tail(&dsp->sendq, skb); + schedule_work(&dsp->workq); + } + spin_unlock_irqrestore(&dsp_lock, flags); + return 0; + } + /* send data to tx-buffer (if no tone is played) */ + if (!dsp->tone.tone) { + spin_lock_irqsave(&dsp_lock, flags); + dsp_cmx_transmit(dsp, skb); + spin_unlock_irqrestore(&dsp_lock, flags); + } + break; + case (PH_CONTROL_REQ): + spin_lock_irqsave(&dsp_lock, flags); + ret = dsp_control_req(dsp, hh, skb); + spin_unlock_irqrestore(&dsp_lock, flags); + break; + case (DL_ESTABLISH_REQ): + case (PH_ACTIVATE_REQ): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: activating b_channel %s\n", + __func__, dsp->name); + if (dsp->dtmf.hardware || dsp->dtmf.software) + dsp_dtmf_goertzel_init(dsp); + get_features(ch); + /* send ph_activate */ + hh->prim = PH_ACTIVATE_REQ; + if (ch->peer) + return ch->recv(ch->peer, skb); + break; + case (DL_RELEASE_REQ): + case (PH_DEACTIVATE_REQ): + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: releasing b_channel %s\n", + __func__, dsp->name); + spin_lock_irqsave(&dsp_lock, flags); + dsp->tone.tone = 0; + dsp->tone.hardware = 0; + dsp->tone.software = 0; + if (timer_pending(&dsp->tone.tl)) + del_timer(&dsp->tone.tl); + if (dsp->conf) + dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be + called here */ + skb_queue_purge(&dsp->sendq); + spin_unlock_irqrestore(&dsp_lock, flags); + hh->prim = PH_DEACTIVATE_REQ; + if (ch->peer) + return ch->recv(ch->peer, skb); + break; + default: + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: msg %x unhandled %s\n", + __func__, hh->prim, dsp->name); + ret = -EINVAL; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static int +dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct dsp *dsp = container_of(ch, struct dsp, ch); + u_long flags; + int err = 0; + + if (debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd); + + switch (cmd) { + case OPEN_CHANNEL: + break; + case CLOSE_CHANNEL: + if (dsp->ch.peer) + dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL); + + /* wait until workqueue has finished, + * must lock here, or we may hit send-process currently + * queueing. */ + spin_lock_irqsave(&dsp_lock, flags); + dsp->b_active = 0; + spin_unlock_irqrestore(&dsp_lock, flags); + /* MUST not be locked, because it waits until queue is done. */ + cancel_work_sync(&dsp->workq); + spin_lock_irqsave(&dsp_lock, flags); + if (timer_pending(&dsp->tone.tl)) + del_timer(&dsp->tone.tl); + skb_queue_purge(&dsp->sendq); + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: releasing member %s\n", + __func__, dsp->name); + dsp->b_active = 0; + dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called + here */ + dsp_pipeline_destroy(&dsp->pipeline); + + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: remove & destroy object %s\n", + __func__, dsp->name); + list_del(&dsp->list); + spin_unlock_irqrestore(&dsp_lock, flags); + + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: dsp instance released\n", + __func__); + vfree(dsp); + module_put(THIS_MODULE); + break; + } + return err; +} + +static void +dsp_send_bh(struct work_struct *work) +{ + struct dsp *dsp = container_of(work, struct dsp, workq); + struct sk_buff *skb; + struct mISDNhead *hh; + + if (dsp->hdlc && dsp->data_pending) + return; /* wait until data has been acknowledged */ + + /* send queued data */ + while ((skb = skb_dequeue(&dsp->sendq))) { + /* in locked date, we must have still data in queue */ + if (dsp->data_pending) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: fifo full %s, this is " + "no bug!\n", __func__, dsp->name); + /* flush transparent data, if not acked */ + dev_kfree_skb(skb); + continue; + } + hh = mISDN_HEAD_P(skb); + if (hh->prim == DL_DATA_REQ) { + /* send packet up */ + if (dsp->up) { + if (dsp->up->send(dsp->up, skb)) + dev_kfree_skb(skb); + } else + dev_kfree_skb(skb); + } else { + /* send packet down */ + if (dsp->ch.peer) { + dsp->data_pending = 1; + if (dsp->ch.recv(dsp->ch.peer, skb)) { + dev_kfree_skb(skb); + dsp->data_pending = 0; + } + } else + dev_kfree_skb(skb); + } + } +} + +static int +dspcreate(struct channel_req *crq) +{ + struct dsp *ndsp; + u_long flags; + + if (crq->protocol != ISDN_P_B_L2DSP + && crq->protocol != ISDN_P_B_L2DSPHDLC) + return -EPROTONOSUPPORT; + ndsp = vmalloc(sizeof(struct dsp)); + if (!ndsp) { + printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__); + return -ENOMEM; + } + memset(ndsp, 0, sizeof(struct dsp)); + if (dsp_debug & DEBUG_DSP_CTRL) + printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__); + + /* default enabled */ + INIT_WORK(&ndsp->workq, (void *)dsp_send_bh); + skb_queue_head_init(&ndsp->sendq); + ndsp->ch.send = dsp_function; + ndsp->ch.ctrl = dsp_ctrl; + ndsp->up = crq->ch; + crq->ch = &ndsp->ch; + if (crq->protocol == ISDN_P_B_L2DSP) { + crq->protocol = ISDN_P_B_RAW; + ndsp->hdlc = 0; + } else { + crq->protocol = ISDN_P_B_HDLC; + ndsp->hdlc = 1; + } + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", + __func__); + + sprintf(ndsp->name, "DSP_C%x(0x%p)", + ndsp->up->st->dev->id + 1, ndsp); + /* set frame size to start */ + ndsp->features.hfc_id = -1; /* current PCM id */ + ndsp->features.pcm_id = -1; /* current PCM id */ + ndsp->pcm_slot_rx = -1; /* current CPM slot */ + ndsp->pcm_slot_tx = -1; + ndsp->pcm_bank_rx = -1; + ndsp->pcm_bank_tx = -1; + ndsp->hfc_conf = -1; /* current conference number */ + /* set tone timer */ + ndsp->tone.tl.function = (void *)dsp_tone_timeout; + ndsp->tone.tl.data = (long) ndsp; + init_timer(&ndsp->tone.tl); + + if (dtmfthreshold < 20 || dtmfthreshold > 500) + dtmfthreshold = 200; + ndsp->dtmf.treshold = dtmfthreshold*10000; + + /* init pipeline append to list */ + spin_lock_irqsave(&dsp_lock, flags); + dsp_pipeline_init(&ndsp->pipeline); + list_add_tail(&ndsp->list, &dsp_ilist); + spin_unlock_irqrestore(&dsp_lock, flags); + + return 0; +} + + +static struct Bprotocol DSP = { + .Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK)) + | (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)), + .name = "dsp", + .create = dspcreate +}; + +static int dsp_init(void) +{ + int err; + int tics; + + printk(KERN_INFO "DSP modul %s\n", mISDN_dsp_revision); + + dsp_options = options; + dsp_debug = debug; + + /* set packet size */ + dsp_poll = poll; + if (dsp_poll) { + if (dsp_poll > MAX_POLL) { + printk(KERN_ERR "%s: Wrong poll value (%d), use %d " + "maximum.\n", __func__, poll, MAX_POLL); + err = -EINVAL; + return err; + } + if (dsp_poll < 8) { + printk(KERN_ERR "%s: Wrong poll value (%d), use 8 " + "minimum.\n", __func__, dsp_poll); + err = -EINVAL; + return err; + } + dsp_tics = poll * HZ / 8000; + if (dsp_tics * 8000 != poll * HZ) { + printk(KERN_INFO "mISDN_dsp: Cannot clock every %d " + "samples (0,125 ms). It is not a multiple of " + "%d HZ.\n", poll, HZ); + err = -EINVAL; + return err; + } + } else { + poll = 8; + while (poll <= MAX_POLL) { + tics = poll * HZ / 8000; + if (tics * 8000 == poll * HZ) { + dsp_tics = tics; + dsp_poll = poll; + if (poll >= 64) + break; + } + poll++; + } + } + if (dsp_poll == 0) { + printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel " + "clock that equals exactly the duration of 8-256 " + "samples. (Choose kernel clock speed like 100, 250, " + "300, 1000)\n"); + err = -EINVAL; + return err; + } + printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals " + "%d jiffies.\n", dsp_poll, dsp_tics); + + spin_lock_init(&dsp_lock); + INIT_LIST_HEAD(&dsp_ilist); + INIT_LIST_HEAD(&conf_ilist); + + /* init conversion tables */ + dsp_audio_generate_law_tables(); + dsp_silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a; + dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32: + dsp_audio_alaw_to_s32; + dsp_audio_generate_s2law_table(); + dsp_audio_generate_seven(); + dsp_audio_generate_mix_table(); + if (dsp_options & DSP_OPT_ULAW) + dsp_audio_generate_ulaw_samples(); + dsp_audio_generate_volume_changes(); + + err = dsp_pipeline_module_init(); + if (err) { + printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, " + "error(%d)\n", err); + return err; + } + + err = mISDN_register_Bprotocol(&DSP); + if (err) { + printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err); + return err; + } + + /* set sample timer */ + dsp_spl_tl.function = (void *)dsp_cmx_send; + dsp_spl_tl.data = 0; + init_timer(&dsp_spl_tl); + dsp_spl_tl.expires = jiffies + dsp_tics; + dsp_spl_jiffies = dsp_spl_tl.expires; + add_timer(&dsp_spl_tl); + + return 0; +} + + +static void dsp_cleanup(void) +{ + mISDN_unregister_Bprotocol(&DSP); + + if (timer_pending(&dsp_spl_tl)) + del_timer(&dsp_spl_tl); + + if (!list_empty(&dsp_ilist)) { + printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not " + "empty.\n"); + } + if (!list_empty(&conf_ilist)) { + printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not " + "all memory freed.\n"); + } + + dsp_pipeline_module_exit(); +} + +module_init(dsp_init); +module_exit(dsp_cleanup); + diff --git a/drivers/isdn/mISDN/dsp_dtmf.c b/drivers/isdn/mISDN/dsp_dtmf.c new file mode 100644 index 000000000000..efc371c1f0dc --- /dev/null +++ b/drivers/isdn/mISDN/dsp_dtmf.c @@ -0,0 +1,303 @@ +/* + * DTMF decoder. + * + * Copyright by Andreas Eversberg (jolly@eversberg.eu) + * based on different decoders such as ISDN4Linux + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include "core.h" +#include "dsp.h" + +#define NCOEFF 8 /* number of frequencies to be analyzed */ + +/* For DTMF recognition: + * 2 * cos(2 * PI * k / N) precalculated for all k + */ +static u64 cos2pik[NCOEFF] = +{ + /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */ + 55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630 +}; + +/* digit matrix */ +static char dtmf_matrix[4][4] = +{ + {'1', '2', '3', 'A'}, + {'4', '5', '6', 'B'}, + {'7', '8', '9', 'C'}, + {'*', '0', '#', 'D'} +}; + +/* dtmf detection using goertzel algorithm + * init function + */ +void dsp_dtmf_goertzel_init(struct dsp *dsp) +{ + dsp->dtmf.size = 0; + dsp->dtmf.lastwhat = '\0'; + dsp->dtmf.lastdigit = '\0'; + dsp->dtmf.count = 0; +} + +/* check for hardware or software features + */ +void dsp_dtmf_hardware(struct dsp *dsp) +{ + int hardware = 1; + + if (!dsp->features.hfc_dtmf) + hardware = 0; + + /* check for volume change */ + if (dsp->tx_volume) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because tx_volume is changed\n", + __func__, dsp->name); + hardware = 0; + } + if (dsp->rx_volume) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because rx_volume is changed\n", + __func__, dsp->name); + hardware = 0; + } + /* check if encryption is enabled */ + if (dsp->bf_enable) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because encryption is enabled\n", + __func__, dsp->name); + hardware = 0; + } + /* check if pipeline exists */ + if (dsp->pipeline.inuse) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, " + "because pipeline exists.\n", + __func__, dsp->name); + hardware = 0; + } + + dsp->dtmf.hardware = hardware; + dsp->dtmf.software = !hardware; +} + + +/************************************************************* + * calculate the coefficients of the given sample and decode * + *************************************************************/ + +/* the given sample is decoded. if the sample is not long enough for a + * complete frame, the decoding is finished and continued with the next + * call of this function. + * + * the algorithm is very good for detection with a minimum of errors. i + * tested it allot. it even works with very short tones (40ms). the only + * disadvantage is, that it doesn't work good with different volumes of both + * tones. this will happen, if accoustically coupled dialers are used. + * it sometimes detects tones during speach, which is normal for decoders. + * use sequences to given commands during calls. + * + * dtmf - points to a structure of the current dtmf state + * spl and len - the sample + * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder + */ + +u8 +*dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt) +{ + u8 what; + int size; + signed short *buf; + s32 sk, sk1, sk2; + int k, n, i; + s32 *hfccoeff; + s32 result[NCOEFF], tresh, treshl; + int lowgroup, highgroup; + s64 cos2pik_; + + dsp->dtmf.digits[0] = '\0'; + + /* Note: The function will loop until the buffer has not enough samples + * left to decode a full frame. + */ +again: + /* convert samples */ + size = dsp->dtmf.size; + buf = dsp->dtmf.buffer; + switch (fmt) { + case 0: /* alaw */ + case 1: /* ulaw */ + while (size < DSP_DTMF_NPOINTS && len) { + buf[size++] = dsp_audio_law_to_s32[*data++]; + len--; + } + break; + + case 2: /* HFC coefficients */ + default: + if (len < 64) { + if (len > 0) + printk(KERN_ERR "%s: coefficients have invalid " + "size. (is=%d < must=%d)\n", + __func__, len, 64); + return dsp->dtmf.digits; + } + hfccoeff = (s32 *)data; + for (k = 0; k < NCOEFF; k++) { + sk2 = (*hfccoeff++)>>4; + sk = (*hfccoeff++)>>4; + if (sk > 32767 || sk < -32767 || sk2 > 32767 + || sk2 < -32767) + printk(KERN_WARNING + "DTMF-Detection overflow\n"); + /* compute |X(k)|**2 */ + result[k] = + (sk * sk) - + (((cos2pik[k] * sk) >> 15) * sk2) + + (sk2 * sk2); + } + data += 64; + len -= 64; + goto coefficients; + break; + } + dsp->dtmf.size = size; + + if (size < DSP_DTMF_NPOINTS) + return dsp->dtmf.digits; + + dsp->dtmf.size = 0; + + /* now we have a full buffer of signed long samples - we do goertzel */ + for (k = 0; k < NCOEFF; k++) { + sk = 0; + sk1 = 0; + sk2 = 0; + buf = dsp->dtmf.buffer; + cos2pik_ = cos2pik[k]; + for (n = 0; n < DSP_DTMF_NPOINTS; n++) { + sk = ((cos2pik_*sk1)>>15) - sk2 + (*buf++); + sk2 = sk1; + sk1 = sk; + } + sk >>= 8; + sk2 >>= 8; + if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767) + printk(KERN_WARNING "DTMF-Detection overflow\n"); + /* compute |X(k)|**2 */ + result[k] = + (sk * sk) - + (((cos2pik[k] * sk) >> 15) * sk2) + + (sk2 * sk2); + } + + /* our (squared) coefficients have been calculated, we need to process + * them. + */ +coefficients: + tresh = 0; + for (i = 0; i < NCOEFF; i++) { + if (result[i] < 0) + result[i] = 0; + if (result[i] > dsp->dtmf.treshold) { + if (result[i] > tresh) + tresh = result[i]; + } + } + + if (tresh == 0) { + what = 0; + goto storedigit; + } + + if (dsp_debug & DEBUG_DSP_DTMFCOEFF) + printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d" + " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n", + result[0]/10000, result[1]/10000, result[2]/10000, + result[3]/10000, result[4]/10000, result[5]/10000, + result[6]/10000, result[7]/10000, tresh/10000, + result[0]/(tresh/100), result[1]/(tresh/100), + result[2]/(tresh/100), result[3]/(tresh/100), + result[4]/(tresh/100), result[5]/(tresh/100), + result[6]/(tresh/100), result[7]/(tresh/100)); + + /* calc digit (lowgroup/highgroup) */ + lowgroup = -1; + highgroup = -1; + treshl = tresh >> 3; /* tones which are not on, must be below 9 dB */ + tresh = tresh >> 2; /* touchtones must match within 6 dB */ + for (i = 0; i < NCOEFF; i++) { + if (result[i] < treshl) + continue; /* ignore */ + if (result[i] < tresh) { + lowgroup = -1; + highgroup = -1; + break; /* noise inbetween */ + } + /* good level found. This is allowed only one time per group */ + if (i < NCOEFF/2) { + /* lowgroup */ + if (lowgroup >= 0) { + /* Bad. Another tone found. */ + lowgroup = -1; + break; + } else + lowgroup = i; + } else { + /* higroup */ + if (highgroup >= 0) { + /* Bad. Another tone found. */ + highgroup = -1; + break; + } else + highgroup = i-(NCOEFF/2); + } + } + + /* get digit or null */ + what = 0; + if (lowgroup >= 0 && highgroup >= 0) + what = dtmf_matrix[lowgroup][highgroup]; + +storedigit: + if (what && (dsp_debug & DEBUG_DSP_DTMF)) + printk(KERN_DEBUG "DTMF what: %c\n", what); + + if (dsp->dtmf.lastwhat != what) + dsp->dtmf.count = 0; + + /* the tone (or no tone) must remain 3 times without change */ + if (dsp->dtmf.count == 2) { + if (dsp->dtmf.lastdigit != what) { + dsp->dtmf.lastdigit = what; + if (what) { + if (dsp_debug & DEBUG_DSP_DTMF) + printk(KERN_DEBUG "DTMF digit: %c\n", + what); + if ((strlen(dsp->dtmf.digits)+1) + < sizeof(dsp->dtmf.digits)) { + dsp->dtmf.digits[strlen( + dsp->dtmf.digits)+1] = '\0'; + dsp->dtmf.digits[strlen( + dsp->dtmf.digits)] = what; + } + } + } + } else + dsp->dtmf.count++; + + dsp->dtmf.lastwhat = what; + + goto again; +} + + diff --git a/drivers/isdn/mISDN/dsp_ecdis.h b/drivers/isdn/mISDN/dsp_ecdis.h new file mode 100644 index 000000000000..8a20af43308b --- /dev/null +++ b/drivers/isdn/mISDN/dsp_ecdis.h @@ -0,0 +1,110 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * ec_disable_detector.h - A detector which should eventually meet the + * G.164/G.165 requirements for detecting the + * 2100Hz echo cancellor disable tone. + * + * Written by Steve Underwood + * + * Copyright (C) 2001 Steve Underwood + * + * 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. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "dsp_biquad.h" + +struct ec_disable_detector_state { + struct biquad2_state notch; + int notch_level; + int channel_level; + int tone_present; + int tone_cycle_duration; + int good_cycles; + int hit; +}; + + +#define FALSE 0 +#define TRUE (!FALSE) + +static inline void +echo_can_disable_detector_init(struct ec_disable_detector_state *det) +{ + /* Elliptic notch */ + /* This is actually centred at 2095Hz, but gets the balance we want, due + to the asymmetric walls of the notch */ + biquad2_init(&det->notch, + (int32_t) (-0.7600000*32768.0), + (int32_t) (-0.1183852*32768.0), + (int32_t) (-0.5104039*32768.0), + (int32_t) (0.1567596*32768.0), + (int32_t) (1.0000000*32768.0)); + + det->channel_level = 0; + det->notch_level = 0; + det->tone_present = FALSE; + det->tone_cycle_duration = 0; + det->good_cycles = 0; + det->hit = 0; +} +/*- End of function --------------------------------------------------------*/ + +static inline int +echo_can_disable_detector_update(struct ec_disable_detector_state *det, +int16_t amp) +{ + int16_t notched; + + notched = biquad2(&det->notch, amp); + /* Estimate the overall energy in the channel, and the energy in + the notch (i.e. overall channel energy - tone energy => noise). + Use abs instead of multiply for speed (is it really faster?). + Damp the overall energy a little more for a stable result. + Damp the notch energy a little less, so we don't damp out the + blip every time the phase reverses */ + det->channel_level += ((abs(amp) - det->channel_level) >> 5); + det->notch_level += ((abs(notched) - det->notch_level) >> 4); + if (det->channel_level > 280) { + /* There is adequate energy in the channel. + Is it mostly at 2100Hz? */ + if (det->notch_level*6 < det->channel_level) { + /* The notch says yes, so we have the tone. */ + if (!det->tone_present) { + /* Do we get a kick every 450+-25ms? */ + if (det->tone_cycle_duration >= 425*8 + && det->tone_cycle_duration <= 475*8) { + det->good_cycles++; + if (det->good_cycles > 2) + det->hit = TRUE; + } + det->tone_cycle_duration = 0; + } + det->tone_present = TRUE; + } else + det->tone_present = FALSE; + det->tone_cycle_duration++; + } else { + det->tone_present = FALSE; + det->tone_cycle_duration = 0; + det->good_cycles = 0; + } + return det->hit; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c new file mode 100644 index 000000000000..eb892d9dd5c6 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_hwec.c @@ -0,0 +1,138 @@ +/* + * dsp_hwec.c: + * builtin mISDN dsp pipeline element for enabling the hw echocanceller + * + * Copyright (C) 2007, Nadi Sarrar + * + * Nadi Sarrar + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + */ + +#include +#include +#include +#include +#include "core.h" +#include "dsp.h" +#include "dsp_hwec.h" + +static struct mISDN_dsp_element_arg args[] = { + { "deftaps", "128", "Set the number of taps of cancellation." }, +}; + +static struct mISDN_dsp_element dsp_hwec_p = { + .name = "hwec", + .new = NULL, + .free = NULL, + .process_tx = NULL, + .process_rx = NULL, + .num_args = sizeof(args) / sizeof(struct mISDN_dsp_element_arg), + .args = args, +}; +struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p; + +void dsp_hwec_enable(struct dsp *dsp, const char *arg) +{ + int deftaps = 128, + len; + struct mISDN_ctrl_req cq; + + if (!dsp) { + printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n", + __func__); + return; + } + + if (!arg) + goto _do; + + len = strlen(arg); + if (!len) + goto _do; + + { + char _dup[len + 1]; + char *dup, *tok, *name, *val; + int tmp; + + strcpy(_dup, arg); + dup = _dup; + + while ((tok = strsep(&dup, ","))) { + if (!strlen(tok)) + continue; + name = strsep(&tok, "="); + val = tok; + + if (!val) + continue; + + if (!strcmp(name, "deftaps")) { + if (sscanf(val, "%d", &tmp) == 1) + deftaps = tmp; + } + } + } + +_do: + printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n", + __func__, deftaps); + memset(&cq, 0, sizeof(cq)); + cq.op = MISDN_CTRL_HFC_ECHOCAN_ON; + cq.p1 = deftaps; + if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } +} + +void dsp_hwec_disable(struct dsp *dsp) +{ + struct mISDN_ctrl_req cq; + + if (!dsp) { + printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n", + __func__); + return; + } + + printk(KERN_DEBUG "%s: disabling hwec\n", __func__); + memset(&cq, 0, sizeof(cq)); + cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF; + if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } +} + +int dsp_hwec_init(void) +{ + mISDN_dsp_element_register(dsp_hwec); + + return 0; +} + +void dsp_hwec_exit(void) +{ + mISDN_dsp_element_unregister(dsp_hwec); +} + diff --git a/drivers/isdn/mISDN/dsp_hwec.h b/drivers/isdn/mISDN/dsp_hwec.h new file mode 100644 index 000000000000..eebe80c3f713 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_hwec.h @@ -0,0 +1,10 @@ +/* + * dsp_hwec.h + */ + +extern struct mISDN_dsp_element *dsp_hwec; +extern void dsp_hwec_enable(struct dsp *dsp, const char *arg); +extern void dsp_hwec_disable(struct dsp *dsp); +extern int dsp_hwec_init(void); +extern void dsp_hwec_exit(void); + diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c new file mode 100644 index 000000000000..850260ab57d0 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_pipeline.c @@ -0,0 +1,348 @@ +/* + * dsp_pipeline.c: pipelined audio processing + * + * Copyright (C) 2007, Nadi Sarrar + * + * Nadi Sarrar + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + */ + +#include +#include +#include +#include +#include +#include "dsp.h" +#include "dsp_hwec.h" + +/* uncomment for debugging */ +/*#define PIPELINE_DEBUG*/ + +struct dsp_pipeline_entry { + struct mISDN_dsp_element *elem; + void *p; + struct list_head list; +}; +struct dsp_element_entry { + struct mISDN_dsp_element *elem; + struct device dev; + struct list_head list; +}; + +static LIST_HEAD(dsp_elements); + +/* sysfs */ +static struct class *elements_class; + +static ssize_t +attr_show_args(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mISDN_dsp_element *elem = dev_get_drvdata(dev); + ssize_t len = 0; + int i = 0; + + *buf = 0; + for (; i < elem->num_args; ++i) + len = sprintf(buf, "%sName: %s\n%s%s%sDescription: %s\n" + "\n", buf, + elem->args[i].name, + elem->args[i].def ? "Default: " : "", + elem->args[i].def ? elem->args[i].def : "", + elem->args[i].def ? "\n" : "", + elem->args[i].desc); + + return len; +} + +static struct device_attribute element_attributes[] = { + __ATTR(args, 0444, attr_show_args, NULL), +}; + +int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) +{ + struct dsp_element_entry *entry; + int ret, i; + + if (!elem) + return -EINVAL; + + entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->elem = elem; + + entry->dev.class = elements_class; + dev_set_drvdata(&entry->dev, elem); + snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name); + ret = device_register(&entry->dev); + if (ret) { + printk(KERN_ERR "%s: failed to register %s\n", + __func__, elem->name); + goto err1; + } + + for (i = 0; i < (sizeof(element_attributes) + / sizeof(struct device_attribute)); ++i) + ret = device_create_file(&entry->dev, + &element_attributes[i]); + if (ret) { + printk(KERN_ERR "%s: failed to create device file\n", + __func__); + goto err2; + } + + list_add_tail(&entry->list, &dsp_elements); + + printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name); + + return 0; + +err2: + device_unregister(&entry->dev); +err1: + kfree(entry); + return ret; +} +EXPORT_SYMBOL(mISDN_dsp_element_register); + +void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) +{ + struct dsp_element_entry *entry, *n; + + if (!elem) + return; + + list_for_each_entry_safe(entry, n, &dsp_elements, list) + if (entry->elem == elem) { + list_del(&entry->list); + device_unregister(&entry->dev); + kfree(entry); + printk(KERN_DEBUG "%s: %s unregistered\n", + __func__, elem->name); + return; + } + printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); +} +EXPORT_SYMBOL(mISDN_dsp_element_unregister); + +int dsp_pipeline_module_init(void) +{ + elements_class = class_create(THIS_MODULE, "dsp_pipeline"); + if (IS_ERR(elements_class)) + return PTR_ERR(elements_class); + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__); +#endif + + dsp_hwec_init(); + + return 0; +} + +void dsp_pipeline_module_exit(void) +{ + struct dsp_element_entry *entry, *n; + + dsp_hwec_exit(); + + class_destroy(elements_class); + + list_for_each_entry_safe(entry, n, &dsp_elements, list) { + list_del(&entry->list); + printk(KERN_WARNING "%s: element was still registered: %s\n", + __func__, entry->elem->name); + kfree(entry); + } + + printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__); +} + +int dsp_pipeline_init(struct dsp_pipeline *pipeline) +{ + if (!pipeline) + return -EINVAL; + + INIT_LIST_HEAD(&pipeline->list); + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__); +#endif + + return 0; +} + +static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline) +{ + struct dsp_pipeline_entry *entry, *n; + + list_for_each_entry_safe(entry, n, &pipeline->list, list) { + list_del(&entry->list); + if (entry->elem == dsp_hwec) + dsp_hwec_disable(container_of(pipeline, struct dsp, + pipeline)); + else + entry->elem->free(entry->p); + kfree(entry); + } +} + +void dsp_pipeline_destroy(struct dsp_pipeline *pipeline) +{ + + if (!pipeline) + return; + + _dsp_pipeline_destroy(pipeline); + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__); +#endif +} + +int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) +{ + int len, incomplete = 0, found = 0; + char *dup, *tok, *name, *args; + struct dsp_element_entry *entry, *n; + struct dsp_pipeline_entry *pipeline_entry; + struct mISDN_dsp_element *elem; + + if (!pipeline) + return -EINVAL; + + if (!list_empty(&pipeline->list)) + _dsp_pipeline_destroy(pipeline); + + if (!cfg) + return 0; + + len = strlen(cfg); + if (!len) + return 0; + + dup = kmalloc(len + 1, GFP_KERNEL); + if (!dup) + return 0; + strcpy(dup, cfg); + while ((tok = strsep(&dup, "|"))) { + if (!strlen(tok)) + continue; + name = strsep(&tok, "("); + args = strsep(&tok, ")"); + if (args && !*args) + args = 0; + + list_for_each_entry_safe(entry, n, &dsp_elements, list) + if (!strcmp(entry->elem->name, name)) { + elem = entry->elem; + + pipeline_entry = kmalloc(sizeof(struct + dsp_pipeline_entry), GFP_KERNEL); + if (!pipeline_entry) { + printk(KERN_DEBUG "%s: failed to add " + "entry to pipeline: %s (out of " + "memory)\n", __func__, elem->name); + incomplete = 1; + goto _out; + } + pipeline_entry->elem = elem; + + if (elem == dsp_hwec) { + /* This is a hack to make the hwec + available as a pipeline module */ + dsp_hwec_enable(container_of(pipeline, + struct dsp, pipeline), args); + list_add_tail(&pipeline_entry->list, + &pipeline->list); + } else { + pipeline_entry->p = elem->new(args); + if (pipeline_entry->p) { + list_add_tail(&pipeline_entry-> + list, &pipeline->list); +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: created " + "instance of %s%s%s\n", + __func__, name, args ? + " with args " : "", args ? + args : ""); +#endif + } else { + printk(KERN_DEBUG "%s: failed " + "to add entry to pipeline: " + "%s (new() returned NULL)\n", + __func__, elem->name); + kfree(pipeline_entry); + incomplete = 1; + } + } + found = 1; + break; + } + + if (found) + found = 0; + else { + printk(KERN_DEBUG "%s: element not found, skipping: " + "%s\n", __func__, name); + incomplete = 1; + } + } + +_out: + if (!list_empty(&pipeline->list)) + pipeline->inuse = 1; + else + pipeline->inuse = 0; + +#ifdef PIPELINE_DEBUG + printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n", + __func__, incomplete ? " incomplete" : "", cfg); +#endif + kfree(dup); + return 0; +} + +void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len) +{ + struct dsp_pipeline_entry *entry; + + if (!pipeline) + return; + + list_for_each_entry(entry, &pipeline->list, list) + if (entry->elem->process_tx) + entry->elem->process_tx(entry->p, data, len); +} + +void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len) +{ + struct dsp_pipeline_entry *entry; + + if (!pipeline) + return; + + list_for_each_entry_reverse(entry, &pipeline->list, list) + if (entry->elem->process_rx) + entry->elem->process_rx(entry->p, data, len); +} + + diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c new file mode 100644 index 000000000000..23dd0dd21524 --- /dev/null +++ b/drivers/isdn/mISDN/dsp_tones.c @@ -0,0 +1,551 @@ +/* + * Audio support data for ISDN4Linux. + * + * Copyright Andreas Eversberg (jolly@eversberg.eu) + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include +#include +#include "core.h" +#include "dsp.h" + + +#define DATA_S sample_silence +#define SIZE_S (&sizeof_silence) +#define DATA_GA sample_german_all +#define SIZE_GA (&sizeof_german_all) +#define DATA_GO sample_german_old +#define SIZE_GO (&sizeof_german_old) +#define DATA_DT sample_american_dialtone +#define SIZE_DT (&sizeof_american_dialtone) +#define DATA_RI sample_american_ringing +#define SIZE_RI (&sizeof_american_ringing) +#define DATA_BU sample_american_busy +#define SIZE_BU (&sizeof_american_busy) +#define DATA_S1 sample_special1 +#define SIZE_S1 (&sizeof_special1) +#define DATA_S2 sample_special2 +#define SIZE_S2 (&sizeof_special2) +#define DATA_S3 sample_special3 +#define SIZE_S3 (&sizeof_special3) + +/***************/ +/* tones loops */ +/***************/ + +/* all tones are alaw encoded */ +/* the last sample+1 is in phase with the first sample. the error is low */ + +static u8 sample_german_all[] = { + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, + 0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d, + 0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c, + 0xdc, 0xfc, 0x6c, +}; +static u32 sizeof_german_all = sizeof(sample_german_all); + +static u8 sample_german_old[] = { + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, + 0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed, + 0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70, + 0x8c, +}; +static u32 sizeof_german_old = sizeof(sample_german_old); + +static u8 sample_american_dialtone[] = { + 0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c, + 0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d, + 0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0, + 0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67, + 0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67, + 0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef, + 0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8, + 0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61, + 0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e, + 0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30, + 0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d, + 0x6d, 0x91, 0x19, +}; +static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone); + +static u8 sample_american_ringing[] = { + 0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90, + 0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed, + 0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c, + 0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d, + 0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec, + 0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11, + 0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00, + 0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39, + 0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6, + 0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3, + 0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b, + 0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f, + 0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56, + 0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59, + 0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30, + 0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d, + 0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c, + 0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd, + 0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc, + 0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d, + 0x4d, 0xbd, 0x0d, 0xad, 0xe1, +}; +static u32 sizeof_american_ringing = sizeof(sample_american_ringing); + +static u8 sample_american_busy[] = { + 0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66, + 0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96, + 0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57, + 0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f, + 0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40, + 0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d, + 0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c, + 0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d, + 0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40, + 0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7, + 0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a, + 0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7, + 0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40, + 0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d, + 0x4d, 0x4d, 0x6d, 0x01, +}; +static u32 sizeof_american_busy = sizeof(sample_american_busy); + +static u8 sample_special1[] = { + 0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d, + 0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd, + 0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd, + 0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd, + 0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed, + 0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41, + 0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7, + 0x6d, 0xbd, 0x2d, +}; +static u32 sizeof_special1 = sizeof(sample_special1); + +static u8 sample_special2[] = { + 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, + 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, + 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, + 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, + 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, + 0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc, + 0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d, + 0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6, + 0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0, + 0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd, +}; +static u32 sizeof_special2 = sizeof(sample_special2); + +static u8 sample_special3[] = { + 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, + 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, + 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, + 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, + 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, + 0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1, + 0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c, + 0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc, + 0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7, + 0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd, +}; +static u32 sizeof_special3 = sizeof(sample_special3); + +static u8 sample_silence[] = { + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, +}; +static u32 sizeof_silence = sizeof(sample_silence); + +struct tones_samples { + u32 *len; + u8 *data; +}; +static struct +tones_samples samples[] = { + {&sizeof_german_all, sample_german_all}, + {&sizeof_german_old, sample_german_old}, + {&sizeof_american_dialtone, sample_american_dialtone}, + {&sizeof_american_ringing, sample_american_ringing}, + {&sizeof_american_busy, sample_american_busy}, + {&sizeof_special1, sample_special1}, + {&sizeof_special2, sample_special2}, + {&sizeof_special3, sample_special3}, + {NULL, NULL}, +}; + +/*********************************** + * generate ulaw from alaw samples * + ***********************************/ + +void +dsp_audio_generate_ulaw_samples(void) +{ + int i, j; + + i = 0; + while (samples[i].len) { + j = 0; + while (j < (*samples[i].len)) { + samples[i].data[j] = + dsp_audio_alaw_to_ulaw[samples[i].data[j]]; + j++; + } + i++; + } +} + + +/**************************** + * tone sequence definition * + ****************************/ + +struct pattern { + int tone; + u8 *data[10]; + u32 *siz[10]; + u32 seq[10]; +} pattern[] = { + {TONE_GERMAN_DIALTONE, + {DATA_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDDIALTONE, + {DATA_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_DIALTONE, + {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_DIALPBX, + {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0}, + {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDDIALPBX, + {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0}, + {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, + + {TONE_AMERICAN_DIALPBX, + {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, 0, 0, 0, 0}, + {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, 0, 0, 0, 0}, + {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} }, + + {TONE_GERMAN_RINGING, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDRINGING, + {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_RINGING, + {DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_RINGPBX, + {DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDRINGPBX, + {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_RINGPBX, + {DATA_RI, DATA_S, DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_BUSY, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDBUSY, + {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_BUSY, + {DATA_BU, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_BU, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_HANGUP, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_OLDHANGUP, + {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_AMERICAN_HANGUP, + {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_SPECIAL_INFO, + {DATA_S1, DATA_S2, DATA_S3, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, 0, 0, 0, 0, 0, 0}, + {2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_GASSENBESETZT, + {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0}, + {2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} }, + + {TONE_GERMAN_AUFSCHALTTON, + {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0}, + {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0}, + {1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} }, + + {0, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, +}; + +/****************** + * copy tone data * + ******************/ + +/* an sk_buff is generated from the number of samples needed. + * the count will be changed and may begin from 0 each pattern period. + * the clue is to precalculate the pointers and legths to use only one + * memcpy per function call, or two memcpy if the tone sequence changes. + * + * pattern - the type of the pattern + * count - the sample from the beginning of the pattern (phase) + * len - the number of bytes + * + * return - the sk_buff with the sample + * + * if tones has finished (e.g. knocking tone), dsp->tones is turned off + */ +void dsp_tone_copy(struct dsp *dsp, u8 *data, int len) +{ + int index, count, start, num; + struct pattern *pat; + struct dsp_tone *tone = &dsp->tone; + + /* if we have no tone, we copy silence */ + if (!tone->tone) { + memset(data, dsp_silence, len); + return; + } + + /* process pattern */ + pat = (struct pattern *)tone->pattern; + /* points to the current pattern */ + index = tone->index; /* gives current sequence index */ + count = tone->count; /* gives current sample */ + + /* copy sample */ + while (len) { + /* find sample to start with */ + while (42) { + /* warp arround */ + if (!pat->seq[index]) { + count = 0; + index = 0; + } + /* check if we are currently playing this tone */ + if (count < pat->seq[index]) + break; + if (dsp_debug & DEBUG_DSP_TONE) + printk(KERN_DEBUG "%s: reaching next sequence " + "(index=%d)\n", __func__, index); + count -= pat->seq[index]; + index++; + } + /* calculate start and number of samples */ + start = count % (*(pat->siz[index])); + num = len; + if (num+count > pat->seq[index]) + num = pat->seq[index] - count; + if (num+start > (*(pat->siz[index]))) + num = (*(pat->siz[index])) - start; + /* copy memory */ + memcpy(data, pat->data[index]+start, num); + /* reduce length */ + data += num; + count += num; + len -= num; + } + tone->index = index; + tone->count = count; + + /* return sk_buff */ + return; +} + + +/******************************* + * send HW message to hfc card * + *******************************/ + +static void +dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len) +{ + struct sk_buff *nskb; + + /* unlocking is not required, because we don't expect a response */ + nskb = _alloc_mISDN_skb(PH_CONTROL_REQ, + (len)?HFC_SPL_LOOP_ON:HFC_SPL_LOOP_OFF, len, sample, + GFP_ATOMIC); + if (nskb) { + if (dsp->ch.peer) { + if (dsp->ch.recv(dsp->ch.peer, nskb)) + dev_kfree_skb(nskb); + } else + dev_kfree_skb(nskb); + } +} + + +/***************** + * timer expires * + *****************/ +void +dsp_tone_timeout(void *arg) +{ + struct dsp *dsp = arg; + struct dsp_tone *tone = &dsp->tone; + struct pattern *pat = (struct pattern *)tone->pattern; + int index = tone->index; + + if (!tone->tone) + return; + + index++; + if (!pat->seq[index]) + index = 0; + tone->index = index; + + /* set next tone */ + if (pat->data[index] == DATA_S) + dsp_tone_hw_message(dsp, 0, 0); + else + dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index])); + /* set timer */ + init_timer(&tone->tl); + tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000; + add_timer(&tone->tl); +} + + +/******************** + * set/release tone * + ********************/ + +/* + * tones are relaized by streaming or by special loop commands if supported + * by hardware. when hardware is used, the patterns will be controlled by + * timers. + */ +int +dsp_tone(struct dsp *dsp, int tone) +{ + struct pattern *pat; + int i; + struct dsp_tone *tonet = &dsp->tone; + + tonet->software = 0; + tonet->hardware = 0; + + /* we turn off the tone */ + if (!tone) { + if (dsp->features.hfc_loops) + if (timer_pending(&tonet->tl)) + del_timer(&tonet->tl); + if (dsp->features.hfc_loops) + dsp_tone_hw_message(dsp, NULL, 0); + tonet->tone = 0; + return 0; + } + + pat = NULL; + i = 0; + while (pattern[i].tone) { + if (pattern[i].tone == tone) { + pat = &pattern[i]; + break; + } + i++; + } + if (!pat) { + printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone); + return -EINVAL; + } + if (dsp_debug & DEBUG_DSP_TONE) + printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n", + __func__, tone, 0); + tonet->tone = tone; + tonet->pattern = pat; + tonet->index = 0; + tonet->count = 0; + + if (dsp->features.hfc_loops) { + tonet->hardware = 1; + /* set first tone */ + dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0])); + /* set timer */ + if (timer_pending(&tonet->tl)) + del_timer(&tonet->tl); + init_timer(&tonet->tl); + tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000; + add_timer(&tonet->tl); + } else { + tonet->software = 1; + } + + return 0; +} + + + + + diff --git a/include/linux/mISDNdsp.h b/include/linux/mISDNdsp.h new file mode 100644 index 000000000000..6b71d2dce508 --- /dev/null +++ b/include/linux/mISDNdsp.h @@ -0,0 +1,37 @@ +#ifndef __mISDNdsp_H__ +#define __mISDNdsp_H__ + +struct mISDN_dsp_element_arg { + char *name; + char *def; + char *desc; +}; + +struct mISDN_dsp_element { + char *name; + void *(*new)(const char *arg); + void (*free)(void *p); + void (*process_tx)(void *p, unsigned char *data, int len); + void (*process_rx)(void *p, unsigned char *data, int len); + int num_args; + struct mISDN_dsp_element_arg + *args; +}; + +extern int mISDN_dsp_element_register(struct mISDN_dsp_element *elem); +extern void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem); + +struct dsp_features { + int hfc_id; /* unique id to identify the chip (or -1) */ + int hfc_dtmf; /* set if HFCmulti card supports dtmf */ + int hfc_loops; /* set if card supports tone loops */ + int hfc_echocanhw; /* set if card supports echocancelation*/ + int pcm_id; /* unique id to identify the pcm bus (or -1) */ + int pcm_slots; /* number of slots on the pcm bus */ + int pcm_banks; /* number of IO banks of pcm bus */ + int unclocked; /* data is not clocked (has jitter/loss) */ + int unordered; /* data is unordered (packets have index) */ +}; + +#endif + -- cgit From af69fb3a8ffa37e986db00ed93099dc44babeef4 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Sun, 27 Jul 2008 02:00:43 +0200 Subject: Add mISDN HFC multiport driver Enable support for cards with Cologne Chip AG's HFC multiport chip. Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/Kconfig | 12 + drivers/isdn/hardware/mISDN/Makefile | 1 + drivers/isdn/hardware/mISDN/hfc_multi.h | 1204 +++++++ drivers/isdn/hardware/mISDN/hfcmulti.c | 5320 +++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 33 + 5 files changed, 6570 insertions(+) create mode 100644 drivers/isdn/hardware/mISDN/hfc_multi.h create mode 100644 drivers/isdn/hardware/mISDN/hfcmulti.c (limited to 'include/linux') diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig index f62dc8752be9..14793480c453 100644 --- a/drivers/isdn/hardware/mISDN/Kconfig +++ b/drivers/isdn/hardware/mISDN/Kconfig @@ -11,3 +11,15 @@ config MISDN_HFCPCI Enable support for cards with Cologne Chip AG's HFC PCI chip. +config MISDN_HFCMULTI + tristate "Support for HFC multiport cards (HFC-4S/8S/E1)" + depends on PCI + depends on MISDN + help + Enable support for cards with Cologne Chip AG's HFC multiport + chip. There are three types of chips that are quite similar, + but the interface is different: + * HFC-4S (4 S/T interfaces on one chip) + * HFC-8S (8 S/T interfaces on one chip) + * HFC-E1 (E1 interface for 2Mbit ISDN) + diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile index 6f20a40b9d54..1e7ca5332ad7 100644 --- a/drivers/isdn/hardware/mISDN/Makefile +++ b/drivers/isdn/hardware/mISDN/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o +obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h new file mode 100644 index 000000000000..a33d87afc843 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -0,0 +1,1204 @@ +/* + * see notice in hfc_multi.c + */ + +extern void ztdummy_extern_interrupt(void); +extern void ztdummy_register_interrupt(void); +extern int ztdummy_unregister_interrupt(void); + +#define DEBUG_HFCMULTI_FIFO 0x00010000 +#define DEBUG_HFCMULTI_CRC 0x00020000 +#define DEBUG_HFCMULTI_INIT 0x00040000 +#define DEBUG_HFCMULTI_PLXSD 0x00080000 +#define DEBUG_HFCMULTI_MODE 0x00100000 +#define DEBUG_HFCMULTI_MSG 0x00200000 +#define DEBUG_HFCMULTI_STATE 0x00400000 +#define DEBUG_HFCMULTI_SYNC 0x01000000 +#define DEBUG_HFCMULTI_DTMF 0x02000000 +#define DEBUG_HFCMULTI_LOCK 0x80000000 + +#define PCI_ENA_REGIO 0x01 +#define PCI_ENA_MEMIO 0x02 + +/* + * NOTE: some registers are assigned multiple times due to different modes + * also registers are assigned differen for HFC-4s/8s and HFC-E1 + */ + +/* +#define MAX_FRAME_SIZE 2048 +*/ + +struct hfc_chan { + struct dchannel *dch; /* link if channel is a D-channel */ + struct bchannel *bch; /* link if channel is a B-channel */ + int port; /* the interface port this */ + /* channel is associated with */ + int nt_timer; /* -1 if off, 0 if elapsed, >0 if running */ + int los, ais, slip_tx, slip_rx, rdi; /* current alarms */ + int jitter; + u_long cfg; /* port configuration */ + int sync; /* sync state (used by E1) */ + u_int protocol; /* current protocol */ + int slot_tx; /* current pcm slot */ + int bank_tx; /* current pcm bank */ + int slot_rx; + int bank_rx; + int conf; /* conference setting of TX slot */ + int txpending; /* if there is currently data in */ + /* the FIFO 0=no, 1=yes, 2=splloop */ + int rx_off; /* set to turn fifo receive off */ + int coeff_count; /* curren coeff block */ + s32 *coeff; /* memory pointer to 8 coeff blocks */ +}; + + +struct hfcm_hw { + u_char r_ctrl; + u_char r_irq_ctrl; + u_char r_cirm; + u_char r_ram_sz; + u_char r_pcm_md0; + u_char r_irqmsk_misc; + u_char r_dtmf; + u_char r_st_sync; + u_char r_sci_msk; + u_char r_tx0, r_tx1; + u_char a_st_ctrl0[8]; + timer_t timer; +}; + + +/* for each stack these flags are used (cfg) */ +#define HFC_CFG_NONCAP_TX 1 /* S/T TX interface has less capacity */ +#define HFC_CFG_DIS_ECHANNEL 2 /* disable E-channel processing */ +#define HFC_CFG_REG_ECHANNEL 3 /* register E-channel */ +#define HFC_CFG_OPTICAL 4 /* the E1 interface is optical */ +#define HFC_CFG_REPORT_LOS 5 /* the card should report loss of signal */ +#define HFC_CFG_REPORT_AIS 6 /* the card should report alarm ind. sign. */ +#define HFC_CFG_REPORT_SLIP 7 /* the card should report bit slips */ +#define HFC_CFG_REPORT_RDI 8 /* the card should report remote alarm */ +#define HFC_CFG_DTMF 9 /* enable DTMF-detection */ +#define HFC_CFG_CRC4 10 /* disable CRC-4 Multiframe mode, */ + /* use double frame instead. */ + +#define HFC_CHIP_EXRAM_128 0 /* external ram 128k */ +#define HFC_CHIP_EXRAM_512 1 /* external ram 256k */ +#define HFC_CHIP_REVISION0 2 /* old fifo handling */ +#define HFC_CHIP_PCM_SLAVE 3 /* PCM is slave */ +#define HFC_CHIP_PCM_MASTER 4 /* PCM is master */ +#define HFC_CHIP_RX_SYNC 5 /* disable pll sync for pcm */ +#define HFC_CHIP_DTMF 6 /* DTMF decoding is enabled */ +#define HFC_CHIP_ULAW 7 /* ULAW mode */ +#define HFC_CHIP_CLOCK2 8 /* double clock mode */ +#define HFC_CHIP_E1CLOCK_GET 9 /* always get clock from E1 interface */ +#define HFC_CHIP_E1CLOCK_PUT 10 /* always put clock from E1 interface */ +#define HFC_CHIP_WATCHDOG 11 /* whether we should send signals */ + /* to the watchdog */ +#define HFC_CHIP_B410P 12 /* whether we have a b410p with echocan in */ + /* hw */ +#define HFC_CHIP_PLXSD 13 /* whether we have a Speech-Design PLX */ + +#define HFC_IO_MODE_PCIMEM 0x00 /* normal memory mapped IO */ +#define HFC_IO_MODE_REGIO 0x01 /* PCI io access */ +#define HFC_IO_MODE_PLXSD 0x02 /* access HFC via PLX9030 */ + +/* table entry in the PCI devices list */ +struct hm_map { + char *vendor_name; + char *card_name; + int type; + int ports; + int clock2; + int leds; + int opticalsupport; + int dip_type; + int io_mode; +}; + +struct hfc_multi { + struct list_head list; + struct hm_map *mtyp; + int id; + int pcm; /* id of pcm bus */ + int type; + int ports; + + u_int irq; /* irq used by card */ + u_int irqcnt; + struct pci_dev *pci_dev; + int io_mode; /* selects mode */ +#ifdef HFC_REGISTER_DEBUG + void (*HFC_outb)(struct hfc_multi *hc, u_char reg, + u_char val, const char *function, int line); + void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, + u_char val, const char *function, int line); + u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + void (*HFC_wait)(struct hfc_multi *hc, + const char *function, int line); + void (*HFC_wait_nodebug)(struct hfc_multi *hc, + const char *function, int line); +#else + void (*HFC_outb)(struct hfc_multi *hc, u_char reg, + u_char val); + void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, + u_char val); + u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg); + u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg); + u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg); + u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg); + void (*HFC_wait)(struct hfc_multi *hc); + void (*HFC_wait_nodebug)(struct hfc_multi *hc); +#endif + void (*read_fifo)(struct hfc_multi *hc, u_char *data, + int len); + void (*write_fifo)(struct hfc_multi *hc, u_char *data, + int len); + u_long pci_origmembase, plx_origmembase, dsp_origmembase; + u_char *pci_membase; /* PCI memory (MUST BE BYTE POINTER) */ + u_char *plx_membase; /* PLX memory */ + u_char *dsp_membase; /* DSP on PLX */ + u_long pci_iobase; /* PCI IO */ + struct hfcm_hw hw; /* remember data of write-only-registers */ + + u_long chip; /* chip configuration */ + int masterclk; /* port that provides master clock -1=off */ + int dtmf; /* flag that dtmf is currently in process */ + int Flen; /* F-buffer size */ + int Zlen; /* Z-buffer size (must be int for calculation)*/ + int max_trans; /* maximum transparent fifo fill */ + int Zmin; /* Z-buffer offset */ + int DTMFbase; /* base address of DTMF coefficients */ + + u_int slots; /* number of PCM slots */ + u_int leds; /* type of leds */ + u_int ledcount; /* used to animate leds */ + u_long ledstate; /* save last state of leds */ + int opticalsupport; /* has the e1 board */ + /* an optical Interface */ + int dslot; /* channel # of d-channel (E1) default 16 */ + + u_long wdcount; /* every 500 ms we need to */ + /* send the watchdog a signal */ + u_char wdbyte; /* watchdog toggle byte */ + u_int activity[8]; /* if there is any action on this */ + /* port (will be cleared after */ + /* showing led-states) */ + int e1_state; /* keep track of last state */ + int e1_getclock; /* if sync is retrieved from interface */ + int syncronized; /* keep track of existing sync interface */ + int e1_resync; /* resync jobs */ + + spinlock_t lock; /* the lock */ + + /* + * the channel index is counted from 0, regardless where the channel + * is located on the hfc-channel. + * the bch->channel is equvalent to the hfc-channel + */ + struct hfc_chan chan[32]; + u_char created[8]; /* what port is created */ + signed char slot_owner[256]; /* owner channel of slot */ +}; + +/* PLX GPIOs */ +#define PLX_GPIO4_DIR_BIT 13 +#define PLX_GPIO4_BIT 14 +#define PLX_GPIO5_DIR_BIT 16 +#define PLX_GPIO5_BIT 17 +#define PLX_GPIO6_DIR_BIT 19 +#define PLX_GPIO6_BIT 20 +#define PLX_GPIO7_DIR_BIT 22 +#define PLX_GPIO7_BIT 23 +#define PLX_GPIO8_DIR_BIT 25 +#define PLX_GPIO8_BIT 26 + +#define PLX_GPIO4 (1 << PLX_GPIO4_BIT) +#define PLX_GPIO5 (1 << PLX_GPIO5_BIT) +#define PLX_GPIO6 (1 << PLX_GPIO6_BIT) +#define PLX_GPIO7 (1 << PLX_GPIO7_BIT) +#define PLX_GPIO8 (1 << PLX_GPIO8_BIT) + +#define PLX_GPIO4_DIR (1 << PLX_GPIO4_DIR_BIT) +#define PLX_GPIO5_DIR (1 << PLX_GPIO5_DIR_BIT) +#define PLX_GPIO6_DIR (1 << PLX_GPIO6_DIR_BIT) +#define PLX_GPIO7_DIR (1 << PLX_GPIO7_DIR_BIT) +#define PLX_GPIO8_DIR (1 << PLX_GPIO8_DIR_BIT) + +#define PLX_TERM_ON PLX_GPIO7 +#define PLX_SLAVE_EN_N PLX_GPIO5 +#define PLX_MASTER_EN PLX_GPIO6 +#define PLX_SYNC_O_EN PLX_GPIO4 +#define PLX_DSP_RES_N PLX_GPIO8 +/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */ +#define PLX_GPIOC_INIT (PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \ + | PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N) + +/* PLX Interrupt Control/STATUS */ +#define PLX_INTCSR_LINTI1_ENABLE 0x01 +#define PLX_INTCSR_LINTI1_STATUS 0x04 +#define PLX_INTCSR_LINTI2_ENABLE 0x08 +#define PLX_INTCSR_LINTI2_STATUS 0x20 +#define PLX_INTCSR_PCIINT_ENABLE 0x40 + +/* PLX Registers */ +#define PLX_INTCSR 0x4c +#define PLX_CNTRL 0x50 +#define PLX_GPIOC 0x54 + + +/* + * REGISTER SETTING FOR HFC-4S/8S AND HFC-E1 + */ + +/* write only registers */ +#define R_CIRM 0x00 +#define R_CTRL 0x01 +#define R_BRG_PCM_CFG 0x02 +#define R_RAM_ADDR0 0x08 +#define R_RAM_ADDR1 0x09 +#define R_RAM_ADDR2 0x0A +#define R_FIRST_FIFO 0x0B +#define R_RAM_SZ 0x0C +#define R_FIFO_MD 0x0D +#define R_INC_RES_FIFO 0x0E +#define R_FSM_IDX 0x0F +#define R_FIFO 0x0F +#define R_SLOT 0x10 +#define R_IRQMSK_MISC 0x11 +#define R_SCI_MSK 0x12 +#define R_IRQ_CTRL 0x13 +#define R_PCM_MD0 0x14 +#define R_PCM_MD1 0x15 +#define R_PCM_MD2 0x15 +#define R_SH0H 0x15 +#define R_SH1H 0x15 +#define R_SH0L 0x15 +#define R_SH1L 0x15 +#define R_SL_SEL0 0x15 +#define R_SL_SEL1 0x15 +#define R_SL_SEL2 0x15 +#define R_SL_SEL3 0x15 +#define R_SL_SEL4 0x15 +#define R_SL_SEL5 0x15 +#define R_SL_SEL6 0x15 +#define R_SL_SEL7 0x15 +#define R_ST_SEL 0x16 +#define R_ST_SYNC 0x17 +#define R_CONF_EN 0x18 +#define R_TI_WD 0x1A +#define R_BERT_WD_MD 0x1B +#define R_DTMF 0x1C +#define R_DTMF_N 0x1D +#define R_E1_WR_STA 0x20 +#define R_E1_RD_STA 0x20 +#define R_LOS0 0x22 +#define R_LOS1 0x23 +#define R_RX0 0x24 +#define R_RX_FR0 0x25 +#define R_RX_FR1 0x26 +#define R_TX0 0x28 +#define R_TX1 0x29 +#define R_TX_FR0 0x2C + +#define R_TX_FR1 0x2D +#define R_TX_FR2 0x2E +#define R_JATT_ATT 0x2F /* undocumented */ +#define A_ST_RD_STATE 0x30 +#define A_ST_WR_STATE 0x30 +#define R_RX_OFF 0x30 +#define A_ST_CTRL0 0x31 +#define R_SYNC_OUT 0x31 +#define A_ST_CTRL1 0x32 +#define A_ST_CTRL2 0x33 +#define A_ST_SQ_WR 0x34 +#define R_TX_OFF 0x34 +#define R_SYNC_CTRL 0x35 +#define A_ST_CLK_DLY 0x37 +#define R_PWM0 0x38 +#define R_PWM1 0x39 +#define A_ST_B1_TX 0x3C +#define A_ST_B2_TX 0x3D +#define A_ST_D_TX 0x3E +#define R_GPIO_OUT0 0x40 +#define R_GPIO_OUT1 0x41 +#define R_GPIO_EN0 0x42 +#define R_GPIO_EN1 0x43 +#define R_GPIO_SEL 0x44 +#define R_BRG_CTRL 0x45 +#define R_PWM_MD 0x46 +#define R_BRG_MD 0x47 +#define R_BRG_TIM0 0x48 +#define R_BRG_TIM1 0x49 +#define R_BRG_TIM2 0x4A +#define R_BRG_TIM3 0x4B +#define R_BRG_TIM_SEL01 0x4C +#define R_BRG_TIM_SEL23 0x4D +#define R_BRG_TIM_SEL45 0x4E +#define R_BRG_TIM_SEL67 0x4F +#define A_SL_CFG 0xD0 +#define A_CONF 0xD1 +#define A_CH_MSK 0xF4 +#define A_CON_HDLC 0xFA +#define A_SUBCH_CFG 0xFB +#define A_CHANNEL 0xFC +#define A_FIFO_SEQ 0xFD +#define A_IRQ_MSK 0xFF + +/* read only registers */ +#define A_Z12 0x04 +#define A_Z1L 0x04 +#define A_Z1 0x04 +#define A_Z1H 0x05 +#define A_Z2L 0x06 +#define A_Z2 0x06 +#define A_Z2H 0x07 +#define A_F1 0x0C +#define A_F12 0x0C +#define A_F2 0x0D +#define R_IRQ_OVIEW 0x10 +#define R_IRQ_MISC 0x11 +#define R_IRQ_STATECH 0x12 +#define R_CONF_OFLOW 0x14 +#define R_RAM_USE 0x15 +#define R_CHIP_ID 0x16 +#define R_BERT_STA 0x17 +#define R_F0_CNTL 0x18 +#define R_F0_CNTH 0x19 +#define R_BERT_EC 0x1A +#define R_BERT_ECL 0x1A +#define R_BERT_ECH 0x1B +#define R_STATUS 0x1C +#define R_CHIP_RV 0x1F +#define R_STATE 0x20 +#define R_SYNC_STA 0x24 +#define R_RX_SL0_0 0x25 +#define R_RX_SL0_1 0x26 +#define R_RX_SL0_2 0x27 +#define R_JATT_DIR 0x2b /* undocumented */ +#define R_SLIP 0x2c +#define A_ST_RD_STA 0x30 +#define R_FAS_EC 0x30 +#define R_FAS_ECL 0x30 +#define R_FAS_ECH 0x31 +#define R_VIO_EC 0x32 +#define R_VIO_ECL 0x32 +#define R_VIO_ECH 0x33 +#define A_ST_SQ_RD 0x34 +#define R_CRC_EC 0x34 +#define R_CRC_ECL 0x34 +#define R_CRC_ECH 0x35 +#define R_E_EC 0x36 +#define R_E_ECL 0x36 +#define R_E_ECH 0x37 +#define R_SA6_SA13_EC 0x38 +#define R_SA6_SA13_ECL 0x38 +#define R_SA6_SA13_ECH 0x39 +#define R_SA6_SA23_EC 0x3A +#define R_SA6_SA23_ECL 0x3A +#define R_SA6_SA23_ECH 0x3B +#define A_ST_B1_RX 0x3C +#define A_ST_B2_RX 0x3D +#define A_ST_D_RX 0x3E +#define A_ST_E_RX 0x3F +#define R_GPIO_IN0 0x40 +#define R_GPIO_IN1 0x41 +#define R_GPI_IN0 0x44 +#define R_GPI_IN1 0x45 +#define R_GPI_IN2 0x46 +#define R_GPI_IN3 0x47 +#define R_INT_DATA 0x88 +#define R_IRQ_FIFO_BL0 0xC8 +#define R_IRQ_FIFO_BL1 0xC9 +#define R_IRQ_FIFO_BL2 0xCA +#define R_IRQ_FIFO_BL3 0xCB +#define R_IRQ_FIFO_BL4 0xCC +#define R_IRQ_FIFO_BL5 0xCD +#define R_IRQ_FIFO_BL6 0xCE +#define R_IRQ_FIFO_BL7 0xCF + +/* read and write registers */ +#define A_FIFO_DATA0 0x80 +#define A_FIFO_DATA1 0x80 +#define A_FIFO_DATA2 0x80 +#define A_FIFO_DATA0_NOINC 0x84 +#define A_FIFO_DATA1_NOINC 0x84 +#define A_FIFO_DATA2_NOINC 0x84 +#define R_RAM_DATA 0xC0 + + +/* + * BIT SETTING FOR HFC-4S/8S AND HFC-E1 + */ + +/* chapter 2: universal bus interface */ +/* R_CIRM */ +#define V_IRQ_SEL 0x01 +#define V_SRES 0x08 +#define V_HFCRES 0x10 +#define V_PCMRES 0x20 +#define V_STRES 0x40 +#define V_ETRES 0x40 +#define V_RLD_EPR 0x80 +/* R_CTRL */ +#define V_FIFO_LPRIO 0x02 +#define V_SLOW_RD 0x04 +#define V_EXT_RAM 0x08 +#define V_CLK_OFF 0x20 +#define V_ST_CLK 0x40 +/* R_RAM_ADDR0 */ +#define V_RAM_ADDR2 0x01 +#define V_ADDR_RES 0x40 +#define V_ADDR_INC 0x80 +/* R_RAM_SZ */ +#define V_RAM_SZ 0x01 +#define V_PWM0_16KHZ 0x10 +#define V_PWM1_16KHZ 0x20 +#define V_FZ_MD 0x80 +/* R_CHIP_ID */ +#define V_PNP_IRQ 0x01 +#define V_CHIP_ID 0x10 + +/* chapter 3: data flow */ +/* R_FIRST_FIFO */ +#define V_FIRST_FIRO_DIR 0x01 +#define V_FIRST_FIFO_NUM 0x02 +/* R_FIFO_MD */ +#define V_FIFO_MD 0x01 +#define V_CSM_MD 0x04 +#define V_FSM_MD 0x08 +#define V_FIFO_SZ 0x10 +/* R_FIFO */ +#define V_FIFO_DIR 0x01 +#define V_FIFO_NUM 0x02 +#define V_REV 0x80 +/* R_SLOT */ +#define V_SL_DIR 0x01 +#define V_SL_NUM 0x02 +/* A_SL_CFG */ +#define V_CH_DIR 0x01 +#define V_CH_SEL 0x02 +#define V_ROUTING 0x40 +/* A_CON_HDLC */ +#define V_IFF 0x01 +#define V_HDLC_TRP 0x02 +#define V_TRP_IRQ 0x04 +#define V_DATA_FLOW 0x20 +/* A_SUBCH_CFG */ +#define V_BIT_CNT 0x01 +#define V_START_BIT 0x08 +#define V_LOOP_FIFO 0x40 +#define V_INV_DATA 0x80 +/* A_CHANNEL */ +#define V_CH_DIR0 0x01 +#define V_CH_NUM0 0x02 +/* A_FIFO_SEQ */ +#define V_NEXT_FIFO_DIR 0x01 +#define V_NEXT_FIFO_NUM 0x02 +#define V_SEQ_END 0x40 + +/* chapter 4: FIFO handling and HDLC controller */ +/* R_INC_RES_FIFO */ +#define V_INC_F 0x01 +#define V_RES_F 0x02 +#define V_RES_LOST 0x04 + +/* chapter 5: S/T interface */ +/* R_SCI_MSK */ +#define V_SCI_MSK_ST0 0x01 +#define V_SCI_MSK_ST1 0x02 +#define V_SCI_MSK_ST2 0x04 +#define V_SCI_MSK_ST3 0x08 +#define V_SCI_MSK_ST4 0x10 +#define V_SCI_MSK_ST5 0x20 +#define V_SCI_MSK_ST6 0x40 +#define V_SCI_MSK_ST7 0x80 +/* R_ST_SEL */ +#define V_ST_SEL 0x01 +#define V_MULT_ST 0x08 +/* R_ST_SYNC */ +#define V_SYNC_SEL 0x01 +#define V_AUTO_SYNC 0x08 +/* A_ST_WR_STA */ +#define V_ST_SET_STA 0x01 +#define V_ST_LD_STA 0x10 +#define V_ST_ACT 0x20 +#define V_SET_G2_G3 0x80 +/* A_ST_CTRL0 */ +#define V_B1_EN 0x01 +#define V_B2_EN 0x02 +#define V_ST_MD 0x04 +#define V_D_PRIO 0x08 +#define V_SQ_EN 0x10 +#define V_96KHZ 0x20 +#define V_TX_LI 0x40 +#define V_ST_STOP 0x80 +/* A_ST_CTRL1 */ +#define V_G2_G3_EN 0x01 +#define V_D_HI 0x04 +#define V_E_IGNO 0x08 +#define V_E_LO 0x10 +#define V_B12_SWAP 0x80 +/* A_ST_CTRL2 */ +#define V_B1_RX_EN 0x01 +#define V_B2_RX_EN 0x02 +#define V_ST_TRIS 0x40 +/* A_ST_CLK_DLY */ +#define V_ST_CK_DLY 0x01 +#define V_ST_SMPL 0x10 +/* A_ST_D_TX */ +#define V_ST_D_TX 0x40 +/* R_IRQ_STATECH */ +#define V_SCI_ST0 0x01 +#define V_SCI_ST1 0x02 +#define V_SCI_ST2 0x04 +#define V_SCI_ST3 0x08 +#define V_SCI_ST4 0x10 +#define V_SCI_ST5 0x20 +#define V_SCI_ST6 0x40 +#define V_SCI_ST7 0x80 +/* A_ST_RD_STA */ +#define V_ST_STA 0x01 +#define V_FR_SYNC_ST 0x10 +#define V_TI2_EXP 0x20 +#define V_INFO0 0x40 +#define V_G2_G3 0x80 +/* A_ST_SQ_RD */ +#define V_ST_SQ 0x01 +#define V_MF_RX_RDY 0x10 +#define V_MF_TX_RDY 0x80 +/* A_ST_D_RX */ +#define V_ST_D_RX 0x40 +/* A_ST_E_RX */ +#define V_ST_E_RX 0x40 + +/* chapter 5: E1 interface */ +/* R_E1_WR_STA */ +/* R_E1_RD_STA */ +#define V_E1_SET_STA 0x01 +#define V_E1_LD_STA 0x10 +/* R_RX0 */ +#define V_RX_CODE 0x01 +#define V_RX_FBAUD 0x04 +#define V_RX_CMI 0x08 +#define V_RX_INV_CMI 0x10 +#define V_RX_INV_CLK 0x20 +#define V_RX_INV_DATA 0x40 +#define V_AIS_ITU 0x80 +/* R_RX_FR0 */ +#define V_NO_INSYNC 0x01 +#define V_AUTO_RESYNC 0x02 +#define V_AUTO_RECO 0x04 +#define V_SWORD_COND 0x08 +#define V_SYNC_LOSS 0x10 +#define V_XCRC_SYNC 0x20 +#define V_MF_RESYNC 0x40 +#define V_RESYNC 0x80 +/* R_RX_FR1 */ +#define V_RX_MF 0x01 +#define V_RX_MF_SYNC 0x02 +#define V_RX_SL0_RAM 0x04 +#define V_ERR_SIM 0x20 +#define V_RES_NMF 0x40 +/* R_TX0 */ +#define V_TX_CODE 0x01 +#define V_TX_FBAUD 0x04 +#define V_TX_CMI_CODE 0x08 +#define V_TX_INV_CMI_CODE 0x10 +#define V_TX_INV_CLK 0x20 +#define V_TX_INV_DATA 0x40 +#define V_OUT_EN 0x80 +/* R_TX1 */ +#define V_INV_CLK 0x01 +#define V_EXCHG_DATA_LI 0x02 +#define V_AIS_OUT 0x04 +#define V_ATX 0x20 +#define V_NTRI 0x40 +#define V_AUTO_ERR_RES 0x80 +/* R_TX_FR0 */ +#define V_TRP_FAS 0x01 +#define V_TRP_NFAS 0x02 +#define V_TRP_RAL 0x04 +#define V_TRP_SA 0x08 +/* R_TX_FR1 */ +#define V_TX_FAS 0x01 +#define V_TX_NFAS 0x02 +#define V_TX_RAL 0x04 +#define V_TX_SA 0x08 +/* R_TX_FR2 */ +#define V_TX_MF 0x01 +#define V_TRP_SL0 0x02 +#define V_TX_SL0_RAM 0x04 +#define V_TX_E 0x10 +#define V_NEG_E 0x20 +#define V_XS12_ON 0x40 +#define V_XS15_ON 0x80 +/* R_RX_OFF */ +#define V_RX_SZ 0x01 +#define V_RX_INIT 0x04 +/* R_SYNC_OUT */ +#define V_SYNC_E1_RX 0x01 +#define V_IPATS0 0x20 +#define V_IPATS1 0x40 +#define V_IPATS2 0x80 +/* R_TX_OFF */ +#define V_TX_SZ 0x01 +#define V_TX_INIT 0x04 +/* R_SYNC_CTRL */ +#define V_EXT_CLK_SYNC 0x01 +#define V_SYNC_OFFS 0x02 +#define V_PCM_SYNC 0x04 +#define V_NEG_CLK 0x08 +#define V_HCLK 0x10 +/* +#define V_JATT_AUTO_DEL 0x20 +#define V_JATT_AUTO 0x40 +*/ +#define V_JATT_OFF 0x80 +/* R_STATE */ +#define V_E1_STA 0x01 +#define V_ALT_FR_RX 0x40 +#define V_ALT_FR_TX 0x80 +/* R_SYNC_STA */ +#define V_RX_STA 0x01 +#define V_FR_SYNC_E1 0x04 +#define V_SIG_LOS 0x08 +#define V_MFA_STA 0x10 +#define V_AIS 0x40 +#define V_NO_MF_SYNC 0x80 +/* R_RX_SL0_0 */ +#define V_SI_FAS 0x01 +#define V_SI_NFAS 0x02 +#define V_A 0x04 +#define V_CRC_OK 0x08 +#define V_TX_E1 0x10 +#define V_TX_E2 0x20 +#define V_RX_E1 0x40 +#define V_RX_E2 0x80 +/* R_SLIP */ +#define V_SLIP_RX 0x01 +#define V_FOSLIP_RX 0x08 +#define V_SLIP_TX 0x10 +#define V_FOSLIP_TX 0x80 + +/* chapter 6: PCM interface */ +/* R_PCM_MD0 */ +#define V_PCM_MD 0x01 +#define V_C4_POL 0x02 +#define V_F0_NEG 0x04 +#define V_F0_LEN 0x08 +#define V_PCM_ADDR 0x10 +/* R_SL_SEL0 */ +#define V_SL_SEL0 0x01 +#define V_SH_SEL0 0x80 +/* R_SL_SEL1 */ +#define V_SL_SEL1 0x01 +#define V_SH_SEL1 0x80 +/* R_SL_SEL2 */ +#define V_SL_SEL2 0x01 +#define V_SH_SEL2 0x80 +/* R_SL_SEL3 */ +#define V_SL_SEL3 0x01 +#define V_SH_SEL3 0x80 +/* R_SL_SEL4 */ +#define V_SL_SEL4 0x01 +#define V_SH_SEL4 0x80 +/* R_SL_SEL5 */ +#define V_SL_SEL5 0x01 +#define V_SH_SEL5 0x80 +/* R_SL_SEL6 */ +#define V_SL_SEL6 0x01 +#define V_SH_SEL6 0x80 +/* R_SL_SEL7 */ +#define V_SL_SEL7 0x01 +#define V_SH_SEL7 0x80 +/* R_PCM_MD1 */ +#define V_ODEC_CON 0x01 +#define V_PLL_ADJ 0x04 +#define V_PCM_DR 0x10 +#define V_PCM_LOOP 0x40 +/* R_PCM_MD2 */ +#define V_SYNC_PLL 0x02 +#define V_SYNC_SRC 0x04 +#define V_SYNC_OUT 0x08 +#define V_ICR_FR_TIME 0x40 +#define V_EN_PLL 0x80 + +/* chapter 7: pulse width modulation */ +/* R_PWM_MD */ +#define V_EXT_IRQ_EN 0x08 +#define V_PWM0_MD 0x10 +#define V_PWM1_MD 0x40 + +/* chapter 8: multiparty audio conferences */ +/* R_CONF_EN */ +#define V_CONF_EN 0x01 +#define V_ULAW 0x80 +/* A_CONF */ +#define V_CONF_NUM 0x01 +#define V_NOISE_SUPPR 0x08 +#define V_ATT_LEV 0x20 +#define V_CONF_SL 0x80 +/* R_CONF_OFLOW */ +#define V_CONF_OFLOW0 0x01 +#define V_CONF_OFLOW1 0x02 +#define V_CONF_OFLOW2 0x04 +#define V_CONF_OFLOW3 0x08 +#define V_CONF_OFLOW4 0x10 +#define V_CONF_OFLOW5 0x20 +#define V_CONF_OFLOW6 0x40 +#define V_CONF_OFLOW7 0x80 + +/* chapter 9: DTMF contoller */ +/* R_DTMF0 */ +#define V_DTMF_EN 0x01 +#define V_HARM_SEL 0x02 +#define V_DTMF_RX_CH 0x04 +#define V_DTMF_STOP 0x08 +#define V_CHBL_SEL 0x10 +#define V_RST_DTMF 0x40 +#define V_ULAW_SEL 0x80 + +/* chapter 10: BERT */ +/* R_BERT_WD_MD */ +#define V_PAT_SEQ 0x01 +#define V_BERT_ERR 0x08 +#define V_AUTO_WD_RES 0x20 +#define V_WD_RES 0x80 +/* R_BERT_STA */ +#define V_BERT_SYNC_SRC 0x01 +#define V_BERT_SYNC 0x10 +#define V_BERT_INV_DATA 0x20 + +/* chapter 11: auxiliary interface */ +/* R_BRG_PCM_CFG */ +#define V_BRG_EN 0x01 +#define V_BRG_MD 0x02 +#define V_PCM_CLK 0x20 +#define V_ADDR_WRDLY 0x40 +/* R_BRG_CTRL */ +#define V_BRG_CS 0x01 +#define V_BRG_ADDR 0x08 +#define V_BRG_CS_SRC 0x80 +/* R_BRG_MD */ +#define V_BRG_MD0 0x01 +#define V_BRG_MD1 0x02 +#define V_BRG_MD2 0x04 +#define V_BRG_MD3 0x08 +#define V_BRG_MD4 0x10 +#define V_BRG_MD5 0x20 +#define V_BRG_MD6 0x40 +#define V_BRG_MD7 0x80 +/* R_BRG_TIM0 */ +#define V_BRG_TIM0_IDLE 0x01 +#define V_BRG_TIM0_CLK 0x10 +/* R_BRG_TIM1 */ +#define V_BRG_TIM1_IDLE 0x01 +#define V_BRG_TIM1_CLK 0x10 +/* R_BRG_TIM2 */ +#define V_BRG_TIM2_IDLE 0x01 +#define V_BRG_TIM2_CLK 0x10 +/* R_BRG_TIM3 */ +#define V_BRG_TIM3_IDLE 0x01 +#define V_BRG_TIM3_CLK 0x10 +/* R_BRG_TIM_SEL01 */ +#define V_BRG_WR_SEL0 0x01 +#define V_BRG_RD_SEL0 0x04 +#define V_BRG_WR_SEL1 0x10 +#define V_BRG_RD_SEL1 0x40 +/* R_BRG_TIM_SEL23 */ +#define V_BRG_WR_SEL2 0x01 +#define V_BRG_RD_SEL2 0x04 +#define V_BRG_WR_SEL3 0x10 +#define V_BRG_RD_SEL3 0x40 +/* R_BRG_TIM_SEL45 */ +#define V_BRG_WR_SEL4 0x01 +#define V_BRG_RD_SEL4 0x04 +#define V_BRG_WR_SEL5 0x10 +#define V_BRG_RD_SEL5 0x40 +/* R_BRG_TIM_SEL67 */ +#define V_BRG_WR_SEL6 0x01 +#define V_BRG_RD_SEL6 0x04 +#define V_BRG_WR_SEL7 0x10 +#define V_BRG_RD_SEL7 0x40 + +/* chapter 12: clock, reset, interrupt, timer and watchdog */ +/* R_IRQMSK_MISC */ +#define V_STA_IRQMSK 0x01 +#define V_TI_IRQMSK 0x02 +#define V_PROC_IRQMSK 0x04 +#define V_DTMF_IRQMSK 0x08 +#define V_IRQ1S_MSK 0x10 +#define V_SA6_IRQMSK 0x20 +#define V_RX_EOMF_MSK 0x40 +#define V_TX_EOMF_MSK 0x80 +/* R_IRQ_CTRL */ +#define V_FIFO_IRQ 0x01 +#define V_GLOB_IRQ_EN 0x08 +#define V_IRQ_POL 0x10 +/* R_TI_WD */ +#define V_EV_TS 0x01 +#define V_WD_TS 0x10 +/* A_IRQ_MSK */ +#define V_IRQ 0x01 +#define V_BERT_EN 0x02 +#define V_MIX_IRQ 0x04 +/* R_IRQ_OVIEW */ +#define V_IRQ_FIFO_BL0 0x01 +#define V_IRQ_FIFO_BL1 0x02 +#define V_IRQ_FIFO_BL2 0x04 +#define V_IRQ_FIFO_BL3 0x08 +#define V_IRQ_FIFO_BL4 0x10 +#define V_IRQ_FIFO_BL5 0x20 +#define V_IRQ_FIFO_BL6 0x40 +#define V_IRQ_FIFO_BL7 0x80 +/* R_IRQ_MISC */ +#define V_STA_IRQ 0x01 +#define V_TI_IRQ 0x02 +#define V_IRQ_PROC 0x04 +#define V_DTMF_IRQ 0x08 +#define V_IRQ1S 0x10 +#define V_SA6_IRQ 0x20 +#define V_RX_EOMF 0x40 +#define V_TX_EOMF 0x80 +/* R_STATUS */ +#define V_BUSY 0x01 +#define V_PROC 0x02 +#define V_DTMF_STA 0x04 +#define V_LOST_STA 0x08 +#define V_SYNC_IN 0x10 +#define V_EXT_IRQSTA 0x20 +#define V_MISC_IRQSTA 0x40 +#define V_FR_IRQSTA 0x80 +/* R_IRQ_FIFO_BL0 */ +#define V_IRQ_FIFO0_TX 0x01 +#define V_IRQ_FIFO0_RX 0x02 +#define V_IRQ_FIFO1_TX 0x04 +#define V_IRQ_FIFO1_RX 0x08 +#define V_IRQ_FIFO2_TX 0x10 +#define V_IRQ_FIFO2_RX 0x20 +#define V_IRQ_FIFO3_TX 0x40 +#define V_IRQ_FIFO3_RX 0x80 +/* R_IRQ_FIFO_BL1 */ +#define V_IRQ_FIFO4_TX 0x01 +#define V_IRQ_FIFO4_RX 0x02 +#define V_IRQ_FIFO5_TX 0x04 +#define V_IRQ_FIFO5_RX 0x08 +#define V_IRQ_FIFO6_TX 0x10 +#define V_IRQ_FIFO6_RX 0x20 +#define V_IRQ_FIFO7_TX 0x40 +#define V_IRQ_FIFO7_RX 0x80 +/* R_IRQ_FIFO_BL2 */ +#define V_IRQ_FIFO8_TX 0x01 +#define V_IRQ_FIFO8_RX 0x02 +#define V_IRQ_FIFO9_TX 0x04 +#define V_IRQ_FIFO9_RX 0x08 +#define V_IRQ_FIFO10_TX 0x10 +#define V_IRQ_FIFO10_RX 0x20 +#define V_IRQ_FIFO11_TX 0x40 +#define V_IRQ_FIFO11_RX 0x80 +/* R_IRQ_FIFO_BL3 */ +#define V_IRQ_FIFO12_TX 0x01 +#define V_IRQ_FIFO12_RX 0x02 +#define V_IRQ_FIFO13_TX 0x04 +#define V_IRQ_FIFO13_RX 0x08 +#define V_IRQ_FIFO14_TX 0x10 +#define V_IRQ_FIFO14_RX 0x20 +#define V_IRQ_FIFO15_TX 0x40 +#define V_IRQ_FIFO15_RX 0x80 +/* R_IRQ_FIFO_BL4 */ +#define V_IRQ_FIFO16_TX 0x01 +#define V_IRQ_FIFO16_RX 0x02 +#define V_IRQ_FIFO17_TX 0x04 +#define V_IRQ_FIFO17_RX 0x08 +#define V_IRQ_FIFO18_TX 0x10 +#define V_IRQ_FIFO18_RX 0x20 +#define V_IRQ_FIFO19_TX 0x40 +#define V_IRQ_FIFO19_RX 0x80 +/* R_IRQ_FIFO_BL5 */ +#define V_IRQ_FIFO20_TX 0x01 +#define V_IRQ_FIFO20_RX 0x02 +#define V_IRQ_FIFO21_TX 0x04 +#define V_IRQ_FIFO21_RX 0x08 +#define V_IRQ_FIFO22_TX 0x10 +#define V_IRQ_FIFO22_RX 0x20 +#define V_IRQ_FIFO23_TX 0x40 +#define V_IRQ_FIFO23_RX 0x80 +/* R_IRQ_FIFO_BL6 */ +#define V_IRQ_FIFO24_TX 0x01 +#define V_IRQ_FIFO24_RX 0x02 +#define V_IRQ_FIFO25_TX 0x04 +#define V_IRQ_FIFO25_RX 0x08 +#define V_IRQ_FIFO26_TX 0x10 +#define V_IRQ_FIFO26_RX 0x20 +#define V_IRQ_FIFO27_TX 0x40 +#define V_IRQ_FIFO27_RX 0x80 +/* R_IRQ_FIFO_BL7 */ +#define V_IRQ_FIFO28_TX 0x01 +#define V_IRQ_FIFO28_RX 0x02 +#define V_IRQ_FIFO29_TX 0x04 +#define V_IRQ_FIFO29_RX 0x08 +#define V_IRQ_FIFO30_TX 0x10 +#define V_IRQ_FIFO30_RX 0x20 +#define V_IRQ_FIFO31_TX 0x40 +#define V_IRQ_FIFO31_RX 0x80 + +/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */ +/* R_GPIO_OUT0 */ +#define V_GPIO_OUT0 0x01 +#define V_GPIO_OUT1 0x02 +#define V_GPIO_OUT2 0x04 +#define V_GPIO_OUT3 0x08 +#define V_GPIO_OUT4 0x10 +#define V_GPIO_OUT5 0x20 +#define V_GPIO_OUT6 0x40 +#define V_GPIO_OUT7 0x80 +/* R_GPIO_OUT1 */ +#define V_GPIO_OUT8 0x01 +#define V_GPIO_OUT9 0x02 +#define V_GPIO_OUT10 0x04 +#define V_GPIO_OUT11 0x08 +#define V_GPIO_OUT12 0x10 +#define V_GPIO_OUT13 0x20 +#define V_GPIO_OUT14 0x40 +#define V_GPIO_OUT15 0x80 +/* R_GPIO_EN0 */ +#define V_GPIO_EN0 0x01 +#define V_GPIO_EN1 0x02 +#define V_GPIO_EN2 0x04 +#define V_GPIO_EN3 0x08 +#define V_GPIO_EN4 0x10 +#define V_GPIO_EN5 0x20 +#define V_GPIO_EN6 0x40 +#define V_GPIO_EN7 0x80 +/* R_GPIO_EN1 */ +#define V_GPIO_EN8 0x01 +#define V_GPIO_EN9 0x02 +#define V_GPIO_EN10 0x04 +#define V_GPIO_EN11 0x08 +#define V_GPIO_EN12 0x10 +#define V_GPIO_EN13 0x20 +#define V_GPIO_EN14 0x40 +#define V_GPIO_EN15 0x80 +/* R_GPIO_SEL */ +#define V_GPIO_SEL0 0x01 +#define V_GPIO_SEL1 0x02 +#define V_GPIO_SEL2 0x04 +#define V_GPIO_SEL3 0x08 +#define V_GPIO_SEL4 0x10 +#define V_GPIO_SEL5 0x20 +#define V_GPIO_SEL6 0x40 +#define V_GPIO_SEL7 0x80 +/* R_GPIO_IN0 */ +#define V_GPIO_IN0 0x01 +#define V_GPIO_IN1 0x02 +#define V_GPIO_IN2 0x04 +#define V_GPIO_IN3 0x08 +#define V_GPIO_IN4 0x10 +#define V_GPIO_IN5 0x20 +#define V_GPIO_IN6 0x40 +#define V_GPIO_IN7 0x80 +/* R_GPIO_IN1 */ +#define V_GPIO_IN8 0x01 +#define V_GPIO_IN9 0x02 +#define V_GPIO_IN10 0x04 +#define V_GPIO_IN11 0x08 +#define V_GPIO_IN12 0x10 +#define V_GPIO_IN13 0x20 +#define V_GPIO_IN14 0x40 +#define V_GPIO_IN15 0x80 +/* R_GPI_IN0 */ +#define V_GPI_IN0 0x01 +#define V_GPI_IN1 0x02 +#define V_GPI_IN2 0x04 +#define V_GPI_IN3 0x08 +#define V_GPI_IN4 0x10 +#define V_GPI_IN5 0x20 +#define V_GPI_IN6 0x40 +#define V_GPI_IN7 0x80 +/* R_GPI_IN1 */ +#define V_GPI_IN8 0x01 +#define V_GPI_IN9 0x02 +#define V_GPI_IN10 0x04 +#define V_GPI_IN11 0x08 +#define V_GPI_IN12 0x10 +#define V_GPI_IN13 0x20 +#define V_GPI_IN14 0x40 +#define V_GPI_IN15 0x80 +/* R_GPI_IN2 */ +#define V_GPI_IN16 0x01 +#define V_GPI_IN17 0x02 +#define V_GPI_IN18 0x04 +#define V_GPI_IN19 0x08 +#define V_GPI_IN20 0x10 +#define V_GPI_IN21 0x20 +#define V_GPI_IN22 0x40 +#define V_GPI_IN23 0x80 +/* R_GPI_IN3 */ +#define V_GPI_IN24 0x01 +#define V_GPI_IN25 0x02 +#define V_GPI_IN26 0x04 +#define V_GPI_IN27 0x08 +#define V_GPI_IN28 0x10 +#define V_GPI_IN29 0x20 +#define V_GPI_IN30 0x40 +#define V_GPI_IN31 0x80 + +/* map of all registers, used for debugging */ + +#ifdef HFC_REGISTER_DEBUG +struct hfc_register_names { + char *name; + u_char reg; +} hfc_register_names[] = { + /* write registers */ + {"R_CIRM", 0x00}, + {"R_CTRL", 0x01}, + {"R_BRG_PCM_CFG ", 0x02}, + {"R_RAM_ADDR0", 0x08}, + {"R_RAM_ADDR1", 0x09}, + {"R_RAM_ADDR2", 0x0A}, + {"R_FIRST_FIFO", 0x0B}, + {"R_RAM_SZ", 0x0C}, + {"R_FIFO_MD", 0x0D}, + {"R_INC_RES_FIFO", 0x0E}, + {"R_FIFO / R_FSM_IDX", 0x0F}, + {"R_SLOT", 0x10}, + {"R_IRQMSK_MISC", 0x11}, + {"R_SCI_MSK", 0x12}, + {"R_IRQ_CTRL", 0x13}, + {"R_PCM_MD0", 0x14}, + {"R_0x15", 0x15}, + {"R_ST_SEL", 0x16}, + {"R_ST_SYNC", 0x17}, + {"R_CONF_EN", 0x18}, + {"R_TI_WD", 0x1A}, + {"R_BERT_WD_MD", 0x1B}, + {"R_DTMF", 0x1C}, + {"R_DTMF_N", 0x1D}, + {"R_E1_XX_STA", 0x20}, + {"R_LOS0", 0x22}, + {"R_LOS1", 0x23}, + {"R_RX0", 0x24}, + {"R_RX_FR0", 0x25}, + {"R_RX_FR1", 0x26}, + {"R_TX0", 0x28}, + {"R_TX1", 0x29}, + {"R_TX_FR0", 0x2C}, + {"R_TX_FR1", 0x2D}, + {"R_TX_FR2", 0x2E}, + {"R_JATT_ATT", 0x2F}, + {"A_ST_xx_STA/R_RX_OFF", 0x30}, + {"A_ST_CTRL0/R_SYNC_OUT", 0x31}, + {"A_ST_CTRL1", 0x32}, + {"A_ST_CTRL2", 0x33}, + {"A_ST_SQ_WR", 0x34}, + {"R_TX_OFF", 0x34}, + {"R_SYNC_CTRL", 0x35}, + {"A_ST_CLK_DLY", 0x37}, + {"R_PWM0", 0x38}, + {"R_PWM1", 0x39}, + {"A_ST_B1_TX", 0x3C}, + {"A_ST_B2_TX", 0x3D}, + {"A_ST_D_TX", 0x3E}, + {"R_GPIO_OUT0", 0x40}, + {"R_GPIO_OUT1", 0x41}, + {"R_GPIO_EN0", 0x42}, + {"R_GPIO_EN1", 0x43}, + {"R_GPIO_SEL", 0x44}, + {"R_BRG_CTRL", 0x45}, + {"R_PWM_MD", 0x46}, + {"R_BRG_MD", 0x47}, + {"R_BRG_TIM0", 0x48}, + {"R_BRG_TIM1", 0x49}, + {"R_BRG_TIM2", 0x4A}, + {"R_BRG_TIM3", 0x4B}, + {"R_BRG_TIM_SEL01", 0x4C}, + {"R_BRG_TIM_SEL23", 0x4D}, + {"R_BRG_TIM_SEL45", 0x4E}, + {"R_BRG_TIM_SEL67", 0x4F}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC", 0x84}, + {"R_RAM_DATA", 0xC0}, + {"A_SL_CFG", 0xD0}, + {"A_CONF", 0xD1}, + {"A_CH_MSK", 0xF4}, + {"A_CON_HDLC", 0xFA}, + {"A_SUBCH_CFG", 0xFB}, + {"A_CHANNEL", 0xFC}, + {"A_FIFO_SEQ", 0xFD}, + {"A_IRQ_MSK", 0xFF}, + {NULL, 0}, + + /* read registers */ + {"A_Z1", 0x04}, + {"A_Z1H", 0x05}, + {"A_Z2", 0x06}, + {"A_Z2H", 0x07}, + {"A_F1", 0x0C}, + {"A_F2", 0x0D}, + {"R_IRQ_OVIEW", 0x10}, + {"R_IRQ_MISC", 0x11}, + {"R_IRQ_STATECH", 0x12}, + {"R_CONF_OFLOW", 0x14}, + {"R_RAM_USE", 0x15}, + {"R_CHIP_ID", 0x16}, + {"R_BERT_STA", 0x17}, + {"R_F0_CNTL", 0x18}, + {"R_F0_CNTH", 0x19}, + {"R_BERT_ECL", 0x1A}, + {"R_BERT_ECH", 0x1B}, + {"R_STATUS", 0x1C}, + {"R_CHIP_RV", 0x1F}, + {"R_STATE", 0x20}, + {"R_SYNC_STA", 0x24}, + {"R_RX_SL0_0", 0x25}, + {"R_RX_SL0_1", 0x26}, + {"R_RX_SL0_2", 0x27}, + {"R_JATT_DIR", 0x2b}, + {"R_SLIP", 0x2c}, + {"A_ST_RD_STA", 0x30}, + {"R_FAS_ECL", 0x30}, + {"R_FAS_ECH", 0x31}, + {"R_VIO_ECL", 0x32}, + {"R_VIO_ECH", 0x33}, + {"R_CRC_ECL / A_ST_SQ_RD", 0x34}, + {"R_CRC_ECH", 0x35}, + {"R_E_ECL", 0x36}, + {"R_E_ECH", 0x37}, + {"R_SA6_SA13_ECL", 0x38}, + {"R_SA6_SA13_ECH", 0x39}, + {"R_SA6_SA23_ECL", 0x3A}, + {"R_SA6_SA23_ECH", 0x3B}, + {"A_ST_B1_RX", 0x3C}, + {"A_ST_B2_RX", 0x3D}, + {"A_ST_D_RX", 0x3E}, + {"A_ST_E_RX", 0x3F}, + {"R_GPIO_IN0", 0x40}, + {"R_GPIO_IN1", 0x41}, + {"R_GPI_IN0", 0x44}, + {"R_GPI_IN1", 0x45}, + {"R_GPI_IN2", 0x46}, + {"R_GPI_IN3", 0x47}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC", 0x84}, + {"R_INT_DATA", 0x88}, + {"R_RAM_DATA", 0xC0}, + {"R_IRQ_FIFO_BL0", 0xC8}, + {"R_IRQ_FIFO_BL1", 0xC9}, + {"R_IRQ_FIFO_BL2", 0xCA}, + {"R_IRQ_FIFO_BL3", 0xCB}, + {"R_IRQ_FIFO_BL4", 0xCC}, + {"R_IRQ_FIFO_BL5", 0xCD}, + {"R_IRQ_FIFO_BL6", 0xCE}, + {"R_IRQ_FIFO_BL7", 0xCF}, +}; +#endif /* HFC_REGISTER_DEBUG */ + diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c new file mode 100644 index 000000000000..2649ea55a9e8 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -0,0 +1,5320 @@ +/* + * hfcmulti.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards + * + * Author Andreas Eversberg (jolly@eversberg.eu) + * ported to mqueue mechanism: + * Peter Sprenger (sprengermoving-bytes.de) + * + * inspired by existing hfc-pci driver: + * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) + * Copyright 2008 by Karsten Keil (kkeil@suse.de) + * Copyright 2008 by Andreas Eversberg (jolly@eversberg.eu) + * + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Thanks to Cologne Chip AG for this great controller! + */ + +/* + * module parameters: + * type: + * By default (0), the card is automatically detected. + * Or use the following combinations: + * Bit 0-7 = 0x00001 = HFC-E1 (1 port) + * or Bit 0-7 = 0x00004 = HFC-4S (4 ports) + * or Bit 0-7 = 0x00008 = HFC-8S (8 ports) + * Bit 8 = 0x00100 = uLaw (instead of aLaw) + * Bit 9 = 0x00200 = Disable DTMF detect on all B-channels via hardware + * Bit 10 = spare + * Bit 11 = 0x00800 = Force PCM bus into slave mode. (otherwhise auto) + * or Bit 12 = 0x01000 = Force PCM bus into master mode. (otherwhise auto) + * Bit 13 = spare + * Bit 14 = 0x04000 = Use external ram (128K) + * Bit 15 = 0x08000 = Use external ram (512K) + * Bit 16 = 0x10000 = Use 64 timeslots instead of 32 + * or Bit 17 = 0x20000 = Use 128 timeslots instead of anything else + * Bit 18 = spare + * Bit 19 = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog) + * (all other bits are reserved and shall be 0) + * example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM + * bus (PCM master) + * + * port: (optional or required for all ports on all installed cards) + * HFC-4S/HFC-8S only bits: + * Bit 0 = 0x001 = Use master clock for this S/T interface + * (ony once per chip). + * Bit 1 = 0x002 = transmitter line setup (non capacitive mode) + * Don't use this unless you know what you are doing! + * Bit 2 = 0x004 = Disable E-channel. (No E-channel processing) + * example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock + * received from port 1 + * + * HFC-E1 only bits: + * Bit 0 = 0x0001 = interface: 0=copper, 1=optical + * Bit 1 = 0x0002 = reserved (later for 32 B-channels transparent mode) + * Bit 2 = 0x0004 = Report LOS + * Bit 3 = 0x0008 = Report AIS + * Bit 4 = 0x0010 = Report SLIP + * Bit 5 = 0x0020 = Report RDI + * Bit 8 = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame + * mode instead. + * Bit 9 = 0x0200 = Force get clock from interface, even in NT mode. + * or Bit 10 = 0x0400 = Force put clock to interface, even in TE mode. + * Bit 11 = 0x0800 = Use direct RX clock for PCM sync rather than PLL. + * (E1 only) + * Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0 + * for default. + * (all other bits are reserved and shall be 0) + * + * debug: + * NOTE: only one debug value must be given for all cards + * enable debugging (see hfc_multi.h for debug options) + * + * poll: + * NOTE: only one poll value must be given for all cards + * Give the number of samples for each fifo process. + * By default 128 is used. Decrease to reduce delay, increase to + * reduce cpu load. If unsure, don't mess with it! + * Valid is 8, 16, 32, 64, 128, 256. + * + * pcm: + * NOTE: only one pcm value must be given for every card. + * The PCM bus id tells the mISDNdsp module about the connected PCM bus. + * By default (0), the PCM bus id is 100 for the card that is PCM master. + * If multiple cards are PCM master (because they are not interconnected), + * each card with PCM master will have increasing PCM id. + * All PCM busses with the same ID are expected to be connected and have + * common time slots slots. + * Only one chip of the PCM bus must be master, the others slave. + * -1 means no support of PCM bus not even. + * Omit this value, if all cards are interconnected or none is connected. + * If unsure, don't give this parameter. + * + * dslot: + * NOTE: only one poll value must be given for every card. + * Also this value must be given for non-E1 cards. If omitted, the E1 + * card has D-channel on time slot 16, which is default. + * If 1..15 or 17..31, an alternate time slot is used for D-channel. + * In this case, the application must be able to handle this. + * If -1 is given, the D-channel is disabled and all 31 slots can be used + * for B-channel. (only for specific applications) + * If you don't know how to use it, you don't need it! + * + * iomode: + * NOTE: only one mode value must be given for every card. + * -> See hfc_multi.h for HFC_IO_MODE_* values + * By default, the IO mode is pci memory IO (MEMIO). + * Some cards requre specific IO mode, so it cannot be changed. + * It may be usefull to set IO mode to register io (REGIO) to solve + * PCI bridge problems. + * If unsure, don't give this parameter. + * + * clockdelay_nt: + * NOTE: only one clockdelay_nt value must be given once for all cards. + * Give the value of the clock control register (A_ST_CLK_DLY) + * of the S/T interfaces in NT mode. + * This register is needed for the TBR3 certification, so don't change it. + * + * clockdelay_te: + * NOTE: only one clockdelay_te value must be given once + * Give the value of the clock control register (A_ST_CLK_DLY) + * of the S/T interfaces in TE mode. + * This register is needed for the TBR3 certification, so don't change it. + */ + +/* + * debug register access (never use this, it will flood your system log) + * #define HFC_REGISTER_DEBUG + */ + +static const char *hfcmulti_revision = "2.00"; + +#include +#include +#include +#include +#include + +/* +#define IRQCOUNT_DEBUG +#define IRQ_DEBUG +*/ + +#include "hfc_multi.h" +#ifdef ECHOPREP +#include "gaintab.h" +#endif + +#define MAX_CARDS 8 +#define MAX_PORTS (8 * MAX_CARDS) + +static LIST_HEAD(HFClist); +static spinlock_t HFClock; /* global hfc list lock */ + +static void ph_state_change(struct dchannel *); +static void (*hfc_interrupt)(void); +static void (*register_interrupt)(void); +static int (*unregister_interrupt)(void); +static int interrupt_registered; + +static struct hfc_multi *syncmaster; +int plxsd_master; /* if we have a master card (yet) */ +static spinlock_t plx_lock; /* may not acquire other lock inside */ +EXPORT_SYMBOL(plx_lock); + +#define TYP_E1 1 +#define TYP_4S 4 +#define TYP_8S 8 + +static int poll_timer = 6; /* default = 128 samples = 16ms */ +/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */ +static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode + (0x60 MUST be included!) */ +static u_char silence = 0xff; /* silence by LAW */ + +#define DIP_4S 0x1 /* DIP Switches for Beronet 1S/2S/4S cards */ +#define DIP_8S 0x2 /* DIP Switches for Beronet 8S+ cards */ +#define DIP_E1 0x3 /* DIP Switches for Beronet E1 cards */ + +/* + * module stuff + */ + +static uint type[MAX_CARDS]; +static uint pcm[MAX_CARDS]; +static uint dslot[MAX_CARDS]; +static uint iomode[MAX_CARDS]; +static uint port[MAX_PORTS]; +static uint debug; +static uint poll; +static uint timer; +static uint clockdelay_te = CLKDEL_TE; +static uint clockdelay_nt = CLKDEL_NT; + +static int HFC_cnt, Port_cnt, PCM_cnt = 99; + +MODULE_AUTHOR("Andreas Eversberg"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(timer, uint, S_IRUGO | S_IWUSR); +module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR); +module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR); +module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); + +#ifdef HFC_REGISTER_DEBUG +#define HFC_outb(hc, reg, val) \ + (hc->HFC_outb(hc, reg, val, __func__, __LINE__)) +#define HFC_outb_nodebug(hc, reg, val) \ + (hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__)) +#define HFC_inb(hc, reg) \ + (hc->HFC_inb(hc, reg, __func__, __LINE__)) +#define HFC_inb_nodebug(hc, reg) \ + (hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__)) +#define HFC_inw(hc, reg) \ + (hc->HFC_inw(hc, reg, __func__, __LINE__)) +#define HFC_inw_nodebug(hc, reg) \ + (hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__)) +#define HFC_wait(hc) \ + (hc->HFC_wait(hc, __func__, __LINE__)) +#define HFC_wait_nodebug(hc) \ + (hc->HFC_wait_nodebug(hc, __func__, __LINE__)) +#else +#define HFC_outb(hc, reg, val) (hc->HFC_outb(hc, reg, val)) +#define HFC_outb_nodebug(hc, reg, val) (hc->HFC_outb_nodebug(hc, reg, val)) +#define HFC_inb(hc, reg) (hc->HFC_inb(hc, reg)) +#define HFC_inb_nodebug(hc, reg) (hc->HFC_inb_nodebug(hc, reg)) +#define HFC_inw(hc, reg) (hc->HFC_inw(hc, reg)) +#define HFC_inw_nodebug(hc, reg) (hc->HFC_inw_nodebug(hc, reg)) +#define HFC_wait(hc) (hc->HFC_wait(hc)) +#define HFC_wait_nodebug(hc) (hc->HFC_wait_nodebug(hc)) +#endif + +/* HFC_IO_MODE_PCIMEM */ +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else +HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + writeb(val, (hc->pci_membase)+reg); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inb_pcimem(struct hfc_multi *hc, u_char reg) +#endif +{ + return readb((hc->pci_membase)+reg); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inw_pcimem(struct hfc_multi *hc, u_char reg) +#endif +{ + return readw((hc->pci_membase)+reg); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line) +#else +HFC_wait_pcimem(struct hfc_multi *hc) +#endif +{ + while (readb((hc->pci_membase)+R_STATUS) & V_BUSY); +} + +/* HFC_IO_MODE_REGIO */ +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else +HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + outb(reg, (hc->pci_iobase)+4); + outb(val, hc->pci_iobase); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inb_regio(struct hfc_multi *hc, u_char reg) +#endif +{ + outb(reg, (hc->pci_iobase)+4); + return inb(hc->pci_iobase); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else +HFC_inw_regio(struct hfc_multi *hc, u_char reg) +#endif +{ + outb(reg, (hc->pci_iobase)+4); + return inw(hc->pci_iobase); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_regio(struct hfc_multi *hc, const char *function, int line) +#else +HFC_wait_regio(struct hfc_multi *hc) +#endif +{ + outb(R_STATUS, (hc->pci_iobase)+4); + while (inb(hc->pci_iobase) & V_BUSY); +} + +#ifdef HFC_REGISTER_DEBUG +static void +HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +{ + char regname[256] = "", bits[9] = "xxxxxxxx"; + int i; + + i = -1; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0'+(!!(val&1)); + bits[6] = '0'+(!!(val&2)); + bits[5] = '0'+(!!(val&4)); + bits[4] = '0'+(!!(val&8)); + bits[3] = '0'+(!!(val&16)); + bits[2] = '0'+(!!(val&32)); + bits[1] = '0'+(!!(val&64)); + bits[0] = '0'+(!!(val&128)); + printk(KERN_DEBUG + "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n", + hc->id, reg, regname, val, bits, function, line); + HFC_outb_nodebug(hc, reg, val); +} +static u_char +HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) +{ + char regname[256] = "", bits[9] = "xxxxxxxx"; + u_char val = HFC_inb_nodebug(hc, reg); + int i; + + i = 0; + while (hfc_register_names[i++].name) + ; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0'+(!!(val&1)); + bits[6] = '0'+(!!(val&2)); + bits[5] = '0'+(!!(val&4)); + bits[4] = '0'+(!!(val&8)); + bits[3] = '0'+(!!(val&16)); + bits[2] = '0'+(!!(val&32)); + bits[1] = '0'+(!!(val&64)); + bits[0] = '0'+(!!(val&128)); + printk(KERN_DEBUG + "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n", + hc->id, reg, regname, val, bits, function, line); + return val; +} +static u_short +HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) +{ + char regname[256] = ""; + u_short val = HFC_inw_nodebug(hc, reg); + int i; + + i = 0; + while (hfc_register_names[i++].name) + ; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + printk(KERN_DEBUG + "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n", + hc->id, reg, regname, val, function, line); + return val; +} +static void +HFC_wait_debug(struct hfc_multi *hc, const char *function, int line) +{ + printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n", + hc->id, function, line); + HFC_wait_nodebug(hc); +} +#endif + +/* write fifo data (REGIO) */ +void +write_fifo_regio(struct hfc_multi *hc, u_char *data, int len) +{ + outb(A_FIFO_DATA0, (hc->pci_iobase)+4); + while (len>>2) { + outl(*(u32 *)data, hc->pci_iobase); + data += 4; + len -= 4; + } + while (len>>1) { + outw(*(u16 *)data, hc->pci_iobase); + data += 2; + len -= 2; + } + while (len) { + outb(*data, hc->pci_iobase); + data++; + len--; + } +} +/* write fifo data (PCIMEM) */ +void +write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) +{ + while (len>>2) { + writel(*(u32 *)data, (hc->pci_membase)+A_FIFO_DATA0); + data += 4; + len -= 4; + } + while (len>>1) { + writew(*(u16 *)data, (hc->pci_membase)+A_FIFO_DATA0); + data += 2; + len -= 2; + } + while (len) { + writeb(*data, (hc->pci_membase)+A_FIFO_DATA0); + data++; + len--; + } +} +/* read fifo data (REGIO) */ +void +read_fifo_regio(struct hfc_multi *hc, u_char *data, int len) +{ + outb(A_FIFO_DATA0, (hc->pci_iobase)+4); + while (len>>2) { + *(u32 *)data = inl(hc->pci_iobase); + data += 4; + len -= 4; + } + while (len>>1) { + *(u16 *)data = inw(hc->pci_iobase); + data += 2; + len -= 2; + } + while (len) { + *data = inb(hc->pci_iobase); + data++; + len--; + } +} + +/* read fifo data (PCIMEM) */ +void +read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) +{ + while (len>>2) { + *(u32 *)data = + readl((hc->pci_membase)+A_FIFO_DATA0); + data += 4; + len -= 4; + } + while (len>>1) { + *(u16 *)data = + readw((hc->pci_membase)+A_FIFO_DATA0); + data += 2; + len -= 2; + } + while (len) { + *data = readb((hc->pci_membase)+A_FIFO_DATA0); + data++; + len--; + } +} + + +static void +enable_hwirq(struct hfc_multi *hc) +{ + hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN; + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); +} + +static void +disable_hwirq(struct hfc_multi *hc) +{ + hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN); + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); +} + +#define NUM_EC 2 +#define MAX_TDM_CHAN 32 + + +inline void +enablepcibridge(struct hfc_multi *c) +{ + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */ +} + +inline void +disablepcibridge(struct hfc_multi *c) +{ + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */ +} + +inline unsigned char +readpcibridge(struct hfc_multi *hc, unsigned char address) +{ + unsigned short cipv; + unsigned char data; + + if (!hc->pci_iobase) + return 0; + + /* slow down a PCI read access by 1 PCI clock cycle */ + HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/ + + if (address == 0) + cipv = 0x4000; + else + cipv = 0x5800; + + /* select local bridge port address by writing to CIP port */ + /* data = HFC_inb(c, cipv); * was _io before */ + outw(cipv, hc->pci_iobase + 4); + data = inb(hc->pci_iobase); + + /* restore R_CTRL for normal PCI read cycle speed */ + HFC_outb(hc, R_CTRL, 0x0); /* was _io before */ + + return data; +} + +inline void +writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data) +{ + unsigned short cipv; + unsigned int datav; + + if (!hc->pci_iobase) + return; + + if (address == 0) + cipv = 0x4000; + else + cipv = 0x5800; + + /* select local bridge port address by writing to CIP port */ + outw(cipv, hc->pci_iobase + 4); + /* define a 32 bit dword with 4 identical bytes for write sequence */ + datav = data | ((__u32) data << 8) | ((__u32) data << 16) | + ((__u32) data << 24); + + /* + * write this 32 bit dword to the bridge data port + * this will initiate a write sequence of up to 4 writes to the same + * address on the local bus interface the number of write accesses + * is undefined but >=1 and depends on the next PCI transaction + * during write sequence on the local bus + */ + outl(datav, hc->pci_iobase); +} + +inline void +cpld_set_reg(struct hfc_multi *hc, unsigned char reg) +{ + /* Do data pin read low byte */ + HFC_outb(hc, R_GPIO_OUT1, reg); +} + +inline void +cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val) +{ + cpld_set_reg(hc, reg); + + enablepcibridge(hc); + writepcibridge(hc, 1, val); + disablepcibridge(hc); + + return; +} + +inline unsigned char +cpld_read_reg(struct hfc_multi *hc, unsigned char reg) +{ + unsigned char bytein; + + cpld_set_reg(hc, reg); + + /* Do data pin read low byte */ + HFC_outb(hc, R_GPIO_OUT1, reg); + + enablepcibridge(hc); + bytein = readpcibridge(hc, 1); + disablepcibridge(hc); + + return bytein; +} + +inline void +vpm_write_address(struct hfc_multi *hc, unsigned short addr) +{ + cpld_write_reg(hc, 0, 0xff & addr); + cpld_write_reg(hc, 1, 0x01 & (addr >> 8)); +} + +inline unsigned short +vpm_read_address(struct hfc_multi *c) +{ + unsigned short addr; + unsigned short highbit; + + addr = cpld_read_reg(c, 0); + highbit = cpld_read_reg(c, 1); + + addr = addr | (highbit << 8); + + return addr & 0x1ff; +} + +inline unsigned char +vpm_in(struct hfc_multi *c, int which, unsigned short addr) +{ + unsigned char res; + + vpm_write_address(c, addr); + + if (!which) + cpld_set_reg(c, 2); + else + cpld_set_reg(c, 3); + + enablepcibridge(c); + res = readpcibridge(c, 1); + disablepcibridge(c); + + cpld_set_reg(c, 0); + + return res; +} + +inline void +vpm_out(struct hfc_multi *c, int which, unsigned short addr, + unsigned char data) +{ + vpm_write_address(c, addr); + + enablepcibridge(c); + + if (!which) + cpld_set_reg(c, 2); + else + cpld_set_reg(c, 3); + + writepcibridge(c, 1, data); + + cpld_set_reg(c, 0); + + disablepcibridge(c); + + { + unsigned char regin; + regin = vpm_in(c, which, addr); + if (regin != data) + printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back " + "0x%x\n", data, addr, regin); + } + +} + + +void +vpm_init(struct hfc_multi *wc) +{ + unsigned char reg; + unsigned int mask; + unsigned int i, x, y; + unsigned int ver; + + for (x = 0; x < NUM_EC; x++) { + /* Setup GPIO's */ + if (!x) { + ver = vpm_in(wc, x, 0x1a0); + printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver); + } + + for (y = 0; y < 4; y++) { + vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ + vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ + vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ + } + + /* Setup TDM path - sets fsync and tdm_clk as inputs */ + reg = vpm_in(wc, x, 0x1a3); /* misc_con */ + vpm_out(wc, x, 0x1a3, reg & ~2); + + /* Setup Echo length (256 taps) */ + vpm_out(wc, x, 0x022, 1); + vpm_out(wc, x, 0x023, 0xff); + + /* Setup timeslots */ + vpm_out(wc, x, 0x02f, 0x00); + mask = 0x02020202 << (x * 4); + + /* Setup the tdm channel masks for all chips */ + for (i = 0; i < 4; i++) + vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff); + + /* Setup convergence rate */ + printk(KERN_DEBUG "VPM: A-law mode\n"); + reg = 0x00 | 0x10 | 0x01; + vpm_out(wc, x, 0x20, reg); + printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg); + /*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */ + + vpm_out(wc, x, 0x24, 0x02); + reg = vpm_in(wc, x, 0x24); + printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg); + + /* Initialize echo cans */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, i, 0x00); + } + + /* + * ARM arch at least disallows a udelay of + * more than 2ms... it gives a fake "__bad_udelay" + * reference at link-time. + * long delays in kernel code are pretty sucky anyway + * for now work around it using 5 x 2ms instead of 1 x 10ms + */ + + udelay(2000); + udelay(2000); + udelay(2000); + udelay(2000); + udelay(2000); + + /* Put in bypass mode */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, i, 0x01); + } + + /* Enable bypass */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, 0x78 + i, 0x01); + } + + } +} + +void +vpm_check(struct hfc_multi *hctmp) +{ + unsigned char gpi2; + + gpi2 = HFC_inb(hctmp, R_GPI_IN2); + + if ((gpi2 & 0x3) != 0x3) + printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2); +} + + +/* + * Interface to enable/disable the HW Echocan + * + * these functions are called within a spin_lock_irqsave on + * the channel instance lock, so we are not disturbed by irqs + * + * we can later easily change the interface to make other + * things configurable, for now we configure the taps + * + */ + +void +vpm_echocan_on(struct hfc_multi *hc, int ch, int taps) +{ + unsigned int timeslot; + unsigned int unit; + struct bchannel *bch = hc->chan[ch].bch; +#ifdef TXADJ + int txadj = -4; + struct sk_buff *skb; +#endif + if (hc->chan[ch].protocol != ISDN_P_B_RAW) + return; + + if (!bch) + return; + +#ifdef TXADJ + skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, + sizeof(int), &txadj, GFP_ATOMIC); + if (skb) + recv_Bchannel_skb(bch, skb); +#endif + + timeslot = ((ch/4)*8) + ((ch%4)*4) + 1; + unit = ch % 4; + + printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n", + taps, timeslot); + + vpm_out(hc, unit, timeslot, 0x7e); +} + +void +vpm_echocan_off(struct hfc_multi *hc, int ch) +{ + unsigned int timeslot; + unsigned int unit; + struct bchannel *bch = hc->chan[ch].bch; +#ifdef TXADJ + int txadj = 0; + struct sk_buff *skb; +#endif + + if (hc->chan[ch].protocol != ISDN_P_B_RAW) + return; + + if (!bch) + return; + +#ifdef TXADJ + skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, + sizeof(int), &txadj, GFP_ATOMIC); + if (skb) + recv_Bchannel_skb(bch, skb); +#endif + + timeslot = ((ch/4)*8) + ((ch%4)*4) + 1; + unit = ch % 4; + + printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n", + timeslot); + /* FILLME */ + vpm_out(hc, unit, timeslot, 0x01); +} + + +/* + * Speech Design resync feature + * NOTE: This is called sometimes outside interrupt handler. + * We must lock irqsave, so no other interrupt (other card) will occurr! + * Also multiple interrupts may nest, so must lock each access (lists, card)! + */ +static inline void +hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm) +{ + struct hfc_multi *hc, *next, *pcmmaster = 0; + u_int *plx_acc_32, pv; + u_long flags; + + spin_lock_irqsave(&HFClock, flags); + spin_lock(&plx_lock); /* must be locked inside other locks */ + + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n", + __func__, syncmaster); + + /* select new master */ + if (newmaster) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "using provided controller\n"); + } else { + list_for_each_entry_safe(hc, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (hc->syncronized) { + newmaster = hc; + break; + } + } + } + } + + /* Disable sync of all cards */ + list_for_each_entry_safe(hc, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv &= ~PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { + pcmmaster = hc; + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Schedule SYNC_I\n"); + hc->e1_resync |= 1; /* get SYNC_I */ + } + } + } + } + + if (newmaster) { + hc = newmaster; + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "id=%d (0x%p) = syncronized with " + "interface.\n", hc->id, hc); + /* Enable new sync master */ + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + /* switch to jatt PLL, if not disabled by RX_SYNC */ + if (hc->type == 1 && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Schedule jatt PLL\n"); + hc->e1_resync |= 2; /* switch to jatt */ + } + } else { + if (pcmmaster) { + hc = pcmmaster; + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "id=%d (0x%p) = PCM master syncronized " + "with QUARTZ\n", hc->id, hc); + if (hc->type == 1) { + /* Use the crystal clock for the PCM + master card */ + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Schedule QUARTZ for HFC-E1\n"); + hc->e1_resync |= 4; /* switch quartz */ + } else { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "QUARTZ is automatically " + "enabled by HFC-%dS\n", hc->type); + } + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + } else + if (!rm) + printk(KERN_ERR "%s no pcm master, this MUST " + "not happen!\n", __func__); + } + syncmaster = newmaster; + + spin_unlock(&plx_lock); + spin_unlock_irqrestore(&HFClock, flags); +} + +/* This must be called AND hc must be locked irqsave!!! */ +inline void +plxsd_checksync(struct hfc_multi *hc, int rm) +{ + if (hc->syncronized) { + if (syncmaster == NULL) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_WARNING "%s: GOT sync on card %d" + " (id=%d)\n", __func__, hc->id + 1, + hc->id); + hfcmulti_resync(hc, hc, rm); + } + } else { + if (syncmaster == hc) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_WARNING "%s: LOST sync on card %d" + " (id=%d)\n", __func__, hc->id + 1, + hc->id); + hfcmulti_resync(hc, NULL, rm); + } + } +} + + +/* + * free hardware resources used by driver + */ +static void +release_io_hfcmulti(struct hfc_multi *hc) +{ + u_int *plx_acc_32, pv; + u_long plx_flags; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + /* soft reset also masks all interrupts */ + hc->hw.r_cirm |= V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(1000); + hc->hw.r_cirm &= ~V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(1000); /* instead of 'wait' that may cause locking */ + + /* release Speech Design card, if PLX was initialized */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: release PLXSD card %d\n", + __func__, hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + writel(PLX_GPIOC_INIT, plx_acc_32); + pv = readl(plx_acc_32); + /* Termination off */ + pv &= ~PLX_TERM_ON; + /* Disconnect the PCM */ + pv |= PLX_SLAVE_EN_N; + pv &= ~PLX_MASTER_EN; + pv &= ~PLX_SYNC_O_EN; + /* Put the DSP in Reset */ + pv &= ~PLX_DSP_RES_N; + writel(pv, plx_acc_32); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: PCM off: PLX_GPIO=%x\n", + __func__, pv); + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + /* disable memory mapped ports / io ports */ + test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */ + pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0); + if (hc->pci_membase) + iounmap((void *)hc->pci_membase); + if (hc->plx_membase) + iounmap((void *)hc->plx_membase); + if (hc->pci_iobase) + release_region(hc->pci_iobase, 8); + + if (hc->pci_dev) { + pci_disable_device(hc->pci_dev); + pci_set_drvdata(hc->pci_dev, NULL); + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); +} + +/* + * function called to reset the HFC chip. A complete software reset of chip + * and fifos is done. All configuration of the chip is done. + */ + +static int +init_chip(struct hfc_multi *hc) +{ + u_long flags, val, val2 = 0, rev; + int i, err = 0; + u_char r_conf_en, rval; + u_int *plx_acc_32, pv; + u_long plx_flags, hfc_flags; + int plx_count; + struct hfc_multi *pos, *next, *plx_last_hc; + + spin_lock_irqsave(&hc->lock, flags); + /* reset all registers */ + memset(&hc->hw, 0, sizeof(struct hfcm_hw)); + + /* revision check */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + val = HFC_inb(hc, R_CHIP_ID)>>4; + if (val != 0x8 && val != 0xc && val != 0xe) { + printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val); + err = -EIO; + goto out; + } + rev = HFC_inb(hc, R_CHIP_RV); + printk(KERN_INFO + "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n", + val, rev, (rev == 0) ? " (old FIFO handling)" : ""); + if (rev == 0) { + test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip); + printk(KERN_WARNING + "HFC_multi: NOTE: Your chip is revision 0, " + "ask Cologne Chip for update. Newer chips " + "have a better FIFO handling. Old chips " + "still work but may have slightly lower " + "HDLC transmit performance.\n"); + } + if (rev > 1) { + printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't " + "consider chip revision = %ld. The chip / " + "bridge may not work.\n", rev); + } + + /* set s-ram size */ + hc->Flen = 0x10; + hc->Zmin = 0x80; + hc->Zlen = 384; + hc->DTMFbase = 0x1000; + if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 128K extenal RAM\n", + __func__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 1; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 1856; + hc->DTMFbase = 0x2000; + } + if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 512K extenal RAM\n", + __func__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 2; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 8000; + hc->DTMFbase = 0x2000; + } + hc->max_trans = poll << 1; + if (hc->max_trans > hc->Zlen) + hc->max_trans = hc->Zlen; + + /* Speech Design PLX bridge */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: initializing PLXSD card %d\n", + __func__, hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + writel(PLX_GPIOC_INIT, plx_acc_32); + pv = readl(plx_acc_32); + /* The first and the last cards are terminating the PCM bus */ + pv |= PLX_TERM_ON; /* hc is currently the last */ + /* Disconnect the PCM */ + pv |= PLX_SLAVE_EN_N; + pv &= ~PLX_MASTER_EN; + pv &= ~PLX_SYNC_O_EN; + /* Put the DSP in Reset */ + pv &= ~PLX_DSP_RES_N; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: slave/term: PLX_GPIO=%x\n", + __func__, pv); + /* + * If we are the 3rd PLXSD card or higher, we must turn + * termination of last PLXSD card off. + */ + spin_lock_irqsave(&HFClock, hfc_flags); + plx_count = 0; + plx_last_hc = NULL; + list_for_each_entry_safe(pos, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) { + plx_count++; + if (pos != hc) + plx_last_hc = pos; + } + } + if (plx_count >= 3) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: card %d is between, so " + "we disable termination\n", + __func__, plx_last_hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(plx_last_hc->plx_membase + + PLX_GPIOC); + pv = readl(plx_acc_32); + pv &= ~PLX_TERM_ON; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: term off: PLX_GPIO=%x\n", + __func__, pv); + } + spin_unlock_irqrestore(&HFClock, hfc_flags); + hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ + } + + /* we only want the real Z2 read-pointer for revision > 0 */ + if (!test_bit(HFC_CHIP_REVISION0, &hc->chip)) + hc->hw.r_ram_sz |= V_FZ_MD; + + /* select pcm mode */ + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into slave mode\n", + __func__); + } else + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into master mode\n", + __func__); + hc->hw.r_pcm_md0 |= V_PCM_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: performing PCM auto detect\n", + __func__); + } + + /* soft reset */ + HFC_outb(hc, R_CTRL, hc->hw.r_ctrl); + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + HFC_outb(hc, R_FIFO_MD, 0); + hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES | V_RLD_EPR; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(100); + hc->hw.r_cirm = 0; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(100); + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + + /* Speech Design PLX bridge pcm and sync mode */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + /* Connect PCM */ + if (hc->hw.r_pcm_md0 & V_PCM_MD) { + pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; + pv |= PLX_SYNC_O_EN; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: master: PLX_GPIO=%x\n", + __func__, pv); + } else { + pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N); + pv &= ~PLX_SYNC_O_EN; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: slave: PLX_GPIO=%x\n", + __func__, pv); + } + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + /* PCM setup */ + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90); + if (hc->slots == 32) + HFC_outb(hc, R_PCM_MD1, 0x00); + if (hc->slots == 64) + HFC_outb(hc, R_PCM_MD1, 0x10); + if (hc->slots == 128) + HFC_outb(hc, R_PCM_MD1, 0x20); + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */ + else + HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */ + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_SLOT, i); + HFC_outb_nodebug(hc, A_SL_CFG, 0); + HFC_outb_nodebug(hc, A_CONF, 0); + hc->slot_owner[i] = -1; + } + + /* set clock speed */ + if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: setting double clock\n", __func__); + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + } + + /* B410P GPIO */ + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + printk(KERN_NOTICE "Setting GPIOs\n"); + HFC_outb(hc, R_GPIO_SEL, 0x30); + HFC_outb(hc, R_GPIO_EN1, 0x3); + udelay(1000); + printk(KERN_NOTICE "calling vpm_init\n"); + vpm_init(hc); + } + + /* check if R_F0_CNT counts (8 kHz frame count) */ + val = HFC_inb(hc, R_F0_CNTL); + val += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "HFC_multi F0_CNT %ld after reset\n", val); + spin_unlock_irqrestore(&hc->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ/100)?:1); /* Timeout minimum 10ms */ + spin_lock_irqsave(&hc->lock, flags); + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "HFC_multi F0_CNT %ld after 10 ms (1st try)\n", + val2); + if (val2 >= val+8) { /* 1 ms */ + /* it counts, so we keep the pcm mode */ + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) + printk(KERN_INFO "controller is PCM bus MASTER\n"); + else + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) + printk(KERN_INFO "controller is PCM bus SLAVE\n"); + else { + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + printk(KERN_INFO "controller is PCM bus SLAVE " + "(auto detected)\n"); + } + } else { + /* does not count */ + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { +controller_fail: + printk(KERN_ERR "HFC_multi ERROR, getting no 125us " + "pulse. Seems that controller fails.\n"); + err = -EIO; + goto out; + } + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_INFO "controller is PCM bus SLAVE " + "(ignoring missing PCM clock)\n"); + } else { + /* only one pcm master */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && plxsd_master) { + printk(KERN_ERR "HFC_multi ERROR, no clock " + "on another Speech Design card found. " + "Please be sure to connect PCM cable.\n"); + err = -EIO; + goto out; + } + /* retry with master clock */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase + + PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: master: PLX_GPIO" + "=%x\n", __func__, pv); + } + hc->hw.r_pcm_md0 |= V_PCM_MD; + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); + spin_unlock_irqrestore(&hc->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ/100)?:1); /* Timeout min. 10ms */ + spin_lock_irqsave(&hc->lock, flags); + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "HFC_multi F0_CNT %ld after " + "10 ms (2nd try)\n", val2); + if (val2 >= val+8) { /* 1 ms */ + test_and_set_bit(HFC_CHIP_PCM_MASTER, + &hc->chip); + printk(KERN_INFO "controller is PCM bus MASTER " + "(auto detected)\n"); + } else + goto controller_fail; + } + } + + /* Release the DSP Reset */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) + plxsd_master = 1; + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC); + pv = readl(plx_acc_32); + pv |= PLX_DSP_RES_N; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: reset off: PLX_GPIO=%x\n", + __func__, pv); + } + + /* pcm id */ + if (hc->pcm) + printk(KERN_INFO "controller has given PCM BUS ID %d\n", + hc->pcm); + else { + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) + || test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + PCM_cnt++; /* SD has proprietary bridging */ + } + hc->pcm = PCM_cnt; + printk(KERN_INFO "controller has PCM BUS ID %d " + "(auto selected)\n", hc->pcm); + } + + /* set up timer */ + HFC_outb(hc, R_TI_WD, poll_timer); + hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; + + /* + * set up 125us interrupt, only if function pointer is available + * and module parameter timer is set + */ + if (timer && hfc_interrupt && register_interrupt) { + /* only one chip should use this interrupt */ + timer = 0; + interrupt_registered = 1; + hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK; + /* deactivate other interrupts in ztdummy */ + register_interrupt(); + } + + /* set E1 state machine IRQ */ + if (hc->type == 1) + hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; + + /* set DTMF detection */ + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: enabling DTMF detection " + "for all B-channel\n", __func__); + hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP; + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + hc->hw.r_dtmf |= V_ULAW_SEL; + HFC_outb(hc, R_DTMF_N, 102 - 1); + hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK; + } + + /* conference engine */ + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + r_conf_en = V_CONF_EN | V_ULAW; + else + r_conf_en = V_CONF_EN; + HFC_outb(hc, R_CONF_EN, r_conf_en); + + /* setting leds */ + switch (hc->leds) { + case 1: /* HFC-E1 OEM */ + if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) + HFC_outb(hc, R_GPIO_SEL, 0x32); + else + HFC_outb(hc, R_GPIO_SEL, 0x30); + + HFC_outb(hc, R_GPIO_EN1, 0x0f); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + + HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); + break; + + case 2: /* HFC-4S OEM */ + case 3: + HFC_outb(hc, R_GPIO_SEL, 0xf0); + HFC_outb(hc, R_GPIO_EN1, 0xff); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + break; + } + + /* set master clock */ + if (hc->masterclk >= 0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting ST master clock " + "to port %d (0..%d)\n", + __func__, hc->masterclk, hc->ports-1); + hc->hw.r_st_sync = hc->masterclk | V_AUTO_SYNC; + HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); + } + + /* setting misc irq */ + HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n", + hc->hw.r_irqmsk_misc); + + /* RAM access test */ + HFC_outb(hc, R_RAM_ADDR0, 0); + HFC_outb(hc, R_RAM_ADDR1, 0); + HFC_outb(hc, R_RAM_ADDR2, 0); + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_RAM_ADDR0, i); + HFC_outb_nodebug(hc, R_RAM_DATA, ((i*3)&0xff)); + } + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_RAM_ADDR0, i); + HFC_inb_nodebug(hc, R_RAM_DATA); + rval = HFC_inb_nodebug(hc, R_INT_DATA); + if (rval != ((i * 3) & 0xff)) { + printk(KERN_DEBUG + "addr:%x val:%x should:%x\n", i, rval, + (i * 3) & 0xff); + err++; + } + } + if (err) { + printk(KERN_DEBUG "aborting - %d RAM access errors\n", err); + err = -EIO; + goto out; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); +out: + spin_unlock_irqrestore(&hc->lock, flags); + return err; +} + + +/* + * control the watchdog + */ +static void +hfcmulti_watchdog(struct hfc_multi *hc) +{ + hc->wdcount++; + + if (hc->wdcount > 10) { + hc->wdcount = 0; + hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ? + V_GPIO_OUT3 : V_GPIO_OUT2; + + /* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */ + HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); + HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte); + } +} + + + +/* + * output leds + */ +static void +hfcmulti_leds(struct hfc_multi *hc) +{ + unsigned long lled; + unsigned long leddw; + int i, state, active, leds; + struct dchannel *dch; + int led[4]; + + hc->ledcount += poll; + if (hc->ledcount > 4096) { + hc->ledcount -= 4096; + hc->ledstate = 0xAFFEAFFE; + } + + switch (hc->leds) { + case 1: /* HFC-E1 OEM */ + /* 2 red blinking: NT mode deactivate + * 2 red steady: TE mode deactivate + * left green: L1 active + * left red: frame sync, but no L1 + * right green: L2 active + */ + if (hc->chan[hc->dslot].sync != 2) { /* no frame sync */ + if (hc->chan[hc->dslot].dch->dev.D.protocol + != ISDN_P_NT_E1) { + led[0] = 1; + led[1] = 1; + } else if (hc->ledcount>>11) { + led[0] = 1; + led[1] = 1; + } else { + led[0] = 0; + led[1] = 0; + } + led[2] = 0; + led[3] = 0; + } else { /* with frame sync */ + /* TODO make it work */ + led[0] = 0; + led[1] = 0; + led[2] = 0; + led[3] = 1; + } + leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF; + /* leds are inverted */ + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_OUT1, leds); + hc->ledstate = leds; + } + break; + + case 2: /* HFC-4S OEM */ + /* red blinking = PH_DEACTIVATE NT Mode + * red steady = PH_DEACTIVATE TE Mode + * green steady = PH_ACTIVATE + */ + for (i = 0; i < 4; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + led[i] = 1; /* led green */ + } else + if (dch->dev.D.protocol == ISDN_P_TE_S0) + /* TE mode: led red */ + led[i] = 2; + else + if (hc->ledcount>>11) + /* led red */ + led[i] = 2; + else + /* led off */ + led[i] = 0; + } else + led[i] = 0; /* led off */ + } + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + leds = 0; + for (i = 0; i < 4; i++) { + if (led[i] == 1) { + /*green*/ + leds |= (0x2 << (i * 2)); + } else if (led[i] == 2) { + /*red*/ + leds |= (0x1 << (i * 2)); + } + } + if (leds != (int)hc->ledstate) { + vpm_out(hc, 0, 0x1a8 + 3, leds); + hc->ledstate = leds; + } + } else { + leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) | + ((led[0] > 0) << 2) | ((led[2] > 0) << 3) | + ((led[3] & 1) << 4) | ((led[1] & 1) << 5) | + ((led[0] & 1) << 6) | ((led[2] & 1) << 7); + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F); + HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4); + hc->ledstate = leds; + } + } + break; + + case 3: /* HFC 1S/2S Beronet */ + /* red blinking = PH_DEACTIVATE NT Mode + * red steady = PH_DEACTIVATE TE Mode + * green steady = PH_ACTIVATE + */ + for (i = 0; i < 2; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + led[i] = 1; /* led green */ + } else + if (dch->dev.D.protocol == ISDN_P_TE_S0) + /* TE mode: led red */ + led[i] = 2; + else + if (hc->ledcount >> 11) + /* led red */ + led[i] = 2; + else + /* led off */ + led[i] = 0; + } else + led[i] = 0; /* led off */ + } + + + leds = (led[0] > 0) | ((led[1] > 0)<<1) | ((led[0]&1)<<2) + | ((led[1]&1)<<3); + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_EN1, + ((led[0] > 0) << 2) | ((led[1] > 0) << 3)); + HFC_outb_nodebug(hc, R_GPIO_OUT1, + ((led[0] & 1) << 2) | ((led[1] & 1) << 3)); + hc->ledstate = leds; + } + break; + case 8: /* HFC 8S+ Beronet */ + lled = 0; + + for (i = 0; i < 8; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + lled |= 0 << i; + } else + if (hc->ledcount >> 11) + lled |= 0 << i; + else + lled |= 1 << i; + } else + lled |= 1 << i; + } + leddw = lled << 24 | lled << 16 | lled << 8 | lled; + if (leddw != hc->ledstate) { + /* HFC_outb(hc, R_BRG_PCM_CFG, 1); + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */ + /* was _io before */ + HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); + outw(0x4000, hc->pci_iobase + 4); + outl(leddw, hc->pci_iobase); + HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK); + hc->ledstate = leddw; + } + break; + } +} +/* + * read dtmf coefficients + */ + +static void +hfcmulti_dtmf(struct hfc_multi *hc) +{ + s32 *coeff; + u_int mantissa; + int co, ch; + struct bchannel *bch = NULL; + u8 exponent; + int dtmf = 0; + int addr; + u16 w_float; + struct sk_buff *skb; + struct mISDNhead *hh; + + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__); + for (ch = 0; ch <= 31; ch++) { + /* only process enabled B-channels */ + bch = hc->chan[ch].bch; + if (!bch) + continue; + if (!hc->created[hc->chan[ch].port]) + continue; + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + continue; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf channel %d:", + __func__, ch); + coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]); + dtmf = 1; + for (co = 0; co < 8; co++) { + /* read W(n-1) coefficient */ + addr = hc->DTMFbase + ((co<<7) | (ch<<2)); + HFC_outb_nodebug(hc, R_RAM_ADDR0, addr); + HFC_outb_nodebug(hc, R_RAM_ADDR1, addr>>8); + HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr>>16) + | V_ADDR_INC); + w_float = HFC_inb_nodebug(hc, R_RAM_DATA); + w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float>>12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent-1); + } + + /* store coefficient */ + coeff[co<<1] = mantissa; + + /* read W(n) coefficient */ + w_float = HFC_inb_nodebug(hc, R_RAM_DATA); + w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float>>12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent-1); + } + + /* store coefficient */ + coeff[(co<<1)|1] = mantissa; + } + if (debug & DEBUG_HFCMULTI_DTMF) + printk("%s: DTMF ready %08x %08x %08x %08x " + "%08x %08x %08x %08x\n", __func__, + coeff[0], coeff[1], coeff[2], coeff[3], + coeff[4], coeff[5], coeff[6], coeff[7]); + hc->chan[ch].coeff_count++; + if (hc->chan[ch].coeff_count == 8) { + hc->chan[ch].coeff_count = 0; + skb = mI_alloc_skb(512, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING "%s: No memory for skb\n", + __func__); + continue; + } + hh = mISDN_HEAD_P(skb); + hh->prim = PH_CONTROL_IND; + hh->id = DTMF_HFC_COEF; + memcpy(skb_put(skb, 512), hc->chan[ch].coeff, 512); + recv_Bchannel_skb(bch, skb); + } + } + + /* restart DTMF processing */ + hc->dtmf = dtmf; + if (dtmf) + HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); +} + + +/* + * fill fifo as much as possible + */ + +static void +hfcmulti_tx(struct hfc_multi *hc, int ch) +{ + int i, ii, temp, len = 0; + int Zspace, z1, z2; /* must be int for calculation */ + int Fspace, f1, f2; + u_char *d; + int *txpending, slot_tx; + struct bchannel *bch; + struct dchannel *dch; + struct sk_buff **sp = NULL; + int *idxp; + + bch = hc->chan[ch].bch; + dch = hc->chan[ch].dch; + if ((!dch) && (!bch)) + return; + + txpending = &hc->chan[ch].txpending; + slot_tx = hc->chan[ch].slot_tx; + if (dch) { + if (!test_bit(FLG_ACTIVE, &dch->Flags)) + return; + sp = &dch->tx_skb; + idxp = &dch->tx_idx; + } else { + if (!test_bit(FLG_ACTIVE, &bch->Flags)) + return; + sp = &bch->tx_skb; + idxp = &bch->tx_idx; + } + if (*sp) + len = (*sp)->len; + + if ((!len) && *txpending != 1) + return; /* no data */ + + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].protocol == ISDN_P_B_RAW) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) + HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1)); + else + HFC_outb_nodebug(hc, R_FIFO, ch << 1); + HFC_wait_nodebug(hc); + + if (*txpending == 2) { + /* reset fifo */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_SUBCH_CFG, 0); + *txpending = 1; + } +next_frame: + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + f1 = HFC_inb_nodebug(hc, A_F1); + f2 = HFC_inb_nodebug(hc, A_F2); + while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): reread f2 because %d!=%d\n", + __func__, hc->id + 1, temp, f2); + f2 = temp; /* repeat until F2 is equal */ + } + Fspace = f2 - f1 - 1; + if (Fspace < 0) + Fspace += hc->Flen; + /* + * Old FIFO handling doesn't give us the current Z2 read + * pointer, so we cannot send the next frame before the fifo + * is empty. It makes no difference except for a slightly + * lower performance. + */ + if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) { + if (f1 != f2) + Fspace = 0; + else + Fspace = 1; + } + /* one frame only for ST D-channels, to allow resending */ + if (hc->type != 1 && dch) { + if (f1 != f2) + Fspace = 0; + } + /* F-counter full condition */ + if (Fspace == 0) + return; + } + z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; + z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; + while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): reread z2 because " + "%d!=%d\n", __func__, hc->id + 1, temp, z2); + z2 = temp; /* repeat unti Z2 is equal */ + } + Zspace = z2 - z1; + if (Zspace <= 0) + Zspace += hc->Zlen; + Zspace -= 4; /* keep not too full, so pointers will not overrun */ + /* fill transparent data only to maxinum transparent load (minus 4) */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + Zspace = Zspace - hc->Zlen + hc->max_trans; + if (Zspace <= 0) /* no space of 4 bytes */ + return; + + /* if no data */ + if (!len) { + if (z1 == z2) { /* empty */ + /* if done with FIFO audio data during PCM connection */ + if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && + *txpending && slot_tx >= 0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: reconnecting PCM due to no " + "more FIFO data: channel %d " + "slot_tx %d\n", + __func__, ch, slot_tx); + /* connect slot */ + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1); + HFC_wait_nodebug(hc); + } + *txpending = 0; + } + return; /* no data */ + } + + /* if audio data and connected slot */ + if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending) + && slot_tx >= 0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: disconnecting PCM due to " + "FIFO data: channel %d slot_tx %d\n", + __func__, ch, slot_tx); + /* disconnect slot */ + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch<<1); + HFC_wait_nodebug(hc); + } + *txpending = 1; + + /* show activity */ + hc->activity[hc->chan[ch].port] = 1; + + /* fill fifo to what we have left */ + ii = len; + if (dch || test_bit(FLG_HDLC, &bch->Flags)) + temp = 1; + else + temp = 0; + i = *idxp; + d = (*sp)->data + i; + if (ii - i > Zspace) + ii = Zspace + i; + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space " + "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n", + __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i, + temp ? "HDLC":"TRANS"); + + + /* Have to prep the audio data */ + hc->write_fifo(hc, d, ii - i); + *idxp = ii; + + /* if not all data has been written */ + if (ii != len) { + /* NOTE: fifo is started by the calling function */ + return; + } + + /* if all data has been written, terminate frame */ + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + /* increment f-counter */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_nodebug(hc); + } + + /* send confirm, since get_net_bframe will not do it with trans */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + confirm_Bsend(bch); + + /* check for next frame */ + dev_kfree_skb(*sp); + if (bch && get_next_bframe(bch)) { /* hdlc is confirmed here */ + len = (*sp)->len; + goto next_frame; + } + if (dch && get_next_dframe(dch)) { + len = (*sp)->len; + goto next_frame; + } + + /* + * now we have no more data, so in case of transparent, + * we set the last byte in fifo to 'silence' in case we will get + * no more data at all. this prevents sending an undefined value. + */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); +} + + +/* NOTE: only called if E1 card is in active state */ +static void +hfcmulti_rx(struct hfc_multi *hc, int ch) +{ + int temp; + int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */ + int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ + int again = 0; + struct bchannel *bch; + struct dchannel *dch; + struct sk_buff *skb, **sp = NULL; + int maxlen; + + bch = hc->chan[ch].bch; + dch = hc->chan[ch].dch; + if ((!dch) && (!bch)) + return; + if (dch) { + if (!test_bit(FLG_ACTIVE, &dch->Flags)) + return; + sp = &dch->rx_skb; + maxlen = dch->maxlen; + } else { + if (!test_bit(FLG_ACTIVE, &bch->Flags)) + return; + sp = &bch->rx_skb; + maxlen = bch->maxlen; + } +next_frame: + /* on first AND before getting next valid frame, R_FIFO must be written + to. */ + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].protocol == ISDN_P_B_RAW) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) + HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch<<1) | 1); + else + HFC_outb_nodebug(hc, R_FIFO, (ch<<1)|1); + HFC_wait_nodebug(hc); + + /* ignore if rx is off BUT change fifo (above) to start pending TX */ + if (hc->chan[ch].rx_off) + return; + + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + f1 = HFC_inb_nodebug(hc, A_F1); + while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): reread f1 because %d!=%d\n", + __func__, hc->id + 1, temp, f1); + f1 = temp; /* repeat until F1 is equal */ + } + f2 = HFC_inb_nodebug(hc, A_F2); + } + z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; + while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): reread z2 because " + "%d!=%d\n", __func__, hc->id + 1, temp, z2); + z1 = temp; /* repeat until Z1 is equal */ + } + z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; + Zsize = z1 - z2; + if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2) + /* complete hdlc frame */ + Zsize++; + if (Zsize < 0) + Zsize += hc->Zlen; + /* if buffer is empty */ + if (Zsize <= 0) + return; + + if (*sp == NULL) { + *sp = mI_alloc_skb(maxlen + 3, GFP_ATOMIC); + if (*sp == NULL) { + printk(KERN_DEBUG "%s: No mem for rx_skb\n", + __func__); + return; + } + } + /* show activity */ + hc->activity[hc->chan[ch].port] = 1; + + /* empty fifo with what we have */ + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d " + "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) " + "got=%d (again %d)\n", __func__, hc->id + 1, ch, + Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE", + f1, f2, Zsize + (*sp)->len, again); + /* HDLC */ + if ((Zsize + (*sp)->len) > (maxlen + 3)) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): hdlc-frame too large.\n", + __func__, hc->id + 1); + skb_trim(*sp, 0); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + return; + } + + hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); + + if (f1 != f2) { + /* increment Z2,F2-counter */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_nodebug(hc); + /* check size */ + if ((*sp)->len < 4) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): Frame below minimum " + "size\n", __func__, hc->id + 1); + skb_trim(*sp, 0); + goto next_frame; + } + /* there is at least one complete frame, check crc */ + if ((*sp)->data[(*sp)->len - 1]) { + if (debug & DEBUG_HFCMULTI_CRC) + printk(KERN_DEBUG + "%s: CRC-error\n", __func__); + skb_trim(*sp, 0); + goto next_frame; + } + skb_trim(*sp, (*sp)->len - 3); + if ((*sp)->len < MISDN_COPY_SIZE) { + skb = *sp; + *sp = mI_alloc_skb(skb->len, GFP_ATOMIC); + if (*sp) { + memcpy(skb_put(*sp, skb->len), + skb->data, skb->len); + skb_trim(skb, 0); + } else { + printk(KERN_DEBUG "%s: No mem\n", + __func__); + *sp = skb; + skb = NULL; + } + } else { + skb = NULL; + } + if (debug & DEBUG_HFCMULTI_FIFO) { + printk(KERN_DEBUG "%s(card %d):", + __func__, hc->id + 1); + temp = 0; + while (temp < (*sp)->len) + printk(" %02x", (*sp)->data[temp++]); + printk("\n"); + } + if (dch) + recv_Dchannel(dch); + else + recv_Bchannel(bch); + *sp = skb; + again++; + goto next_frame; + } + /* there is an incomplete frame */ + } else { + /* transparent */ + if (Zsize > skb_tailroom(*sp)) + Zsize = skb_tailroom(*sp); + hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); + if (((*sp)->len) < MISDN_COPY_SIZE) { + skb = *sp; + *sp = mI_alloc_skb(skb->len, GFP_ATOMIC); + if (*sp) { + memcpy(skb_put(*sp, skb->len), + skb->data, skb->len); + skb_trim(skb, 0); + } else { + printk(KERN_DEBUG "%s: No mem\n", __func__); + *sp = skb; + skb = NULL; + } + } else { + skb = NULL; + } + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): fifo(%d) reading %d bytes " + "(z1=%04x, z2=%04x) TRANS\n", + __func__, hc->id + 1, ch, Zsize, z1, z2); + /* only bch is transparent */ + recv_Bchannel(bch); + *sp = skb; + } +} + + +/* + * Interrupt handler + */ +static void +signal_state_up(struct dchannel *dch, int info, char *msg) +{ + struct sk_buff *skb; + int id, data = info; + + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: %s\n", __func__, msg); + + id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */ + + skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data, + GFP_ATOMIC); + if (!skb) + return; + recv_Dchannel_skb(dch, skb); +} + +static inline void +handle_timer_irq(struct hfc_multi *hc) +{ + int ch, temp; + struct dchannel *dch; + u_long flags; + + /* process queued resync jobs */ + if (hc->e1_resync) { + /* lock, so e1_resync gets not changed */ + spin_lock_irqsave(&HFClock, flags); + if (hc->e1_resync & 1) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Enable SYNC_I\n"); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC); + /* disable JATT, if RX_SYNC is set */ + if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) + HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); + } + if (hc->e1_resync & 2) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Enable jatt PLL\n"); + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } + if (hc->e1_resync & 4) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Enable QUARTZ for HFC-E1\n"); + /* set jatt to quartz */ + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC + | V_JATT_OFF); + /* switch to JATT, in case it is not already */ + HFC_outb(hc, R_SYNC_OUT, 0); + } + hc->e1_resync = 0; + spin_unlock_irqrestore(&HFClock, flags); + } + + if (hc->type != 1 || hc->e1_state == 1) + for (ch = 0; ch <= 31; ch++) { + if (hc->created[hc->chan[ch].port]) { + hfcmulti_tx(hc, ch); + /* fifo is started when switching to rx-fifo */ + hfcmulti_rx(hc, ch); + if (hc->chan[ch].dch && + hc->chan[ch].nt_timer > -1) { + dch = hc->chan[ch].dch; + if (!(--hc->chan[ch].nt_timer)) { + schedule_event(dch, + FLG_PHCHANGE); + if (debug & + DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: nt_timer at " + "state %x\n", + __func__, + dch->state); + } + } + } + } + if (hc->type == 1 && hc->created[0]) { + dch = hc->chan[hc->dslot].dch; + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) { + /* LOS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS; + if (!temp && hc->chan[hc->dslot].los) + signal_state_up(dch, L1_SIGNAL_LOS_ON, + "LOS detected"); + if (temp && !hc->chan[hc->dslot].los) + signal_state_up(dch, L1_SIGNAL_LOS_OFF, + "LOS gone"); + hc->chan[hc->dslot].los = temp; + } + if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dslot].cfg)) { + /* AIS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS; + if (!temp && hc->chan[hc->dslot].ais) + signal_state_up(dch, L1_SIGNAL_AIS_ON, + "AIS detected"); + if (temp && !hc->chan[hc->dslot].ais) + signal_state_up(dch, L1_SIGNAL_AIS_OFF, + "AIS gone"); + hc->chan[hc->dslot].ais = temp; + } + if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dslot].cfg)) { + /* SLIP */ + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX; + if (!temp && hc->chan[hc->dslot].slip_rx) + signal_state_up(dch, L1_SIGNAL_SLIP_RX, + " bit SLIP detected RX"); + hc->chan[hc->dslot].slip_rx = temp; + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX; + if (!temp && hc->chan[hc->dslot].slip_tx) + signal_state_up(dch, L1_SIGNAL_SLIP_TX, + " bit SLIP detected TX"); + hc->chan[hc->dslot].slip_tx = temp; + } + if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dslot].cfg)) { + /* RDI */ + temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A; + if (!temp && hc->chan[hc->dslot].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_ON, + "RDI detected"); + if (temp && !hc->chan[hc->dslot].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_OFF, + "RDI gone"); + hc->chan[hc->dslot].rdi = temp; + } + temp = HFC_inb_nodebug(hc, R_JATT_DIR); + switch (hc->chan[hc->dslot].sync) { + case 0: + if ((temp & 0x60) == 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 now " + "in clock sync\n", + __func__, hc->id); + HFC_outb(hc, R_RX_OFF, + hc->chan[hc->dslot].jitter | V_RX_INIT); + HFC_outb(hc, R_TX_OFF, + hc->chan[hc->dslot].jitter | V_RX_INIT); + hc->chan[hc->dslot].sync = 1; + goto check_framesync; + } + break; + case 1: + if ((temp & 0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "lost clock sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 0; + break; + } +check_framesync: + temp = HFC_inb_nodebug(hc, R_SYNC_STA); + if (temp == 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "now in frame sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 2; + } + break; + case 2: + if ((temp & 0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 lost " + "clock & frame sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 0; + break; + } + temp = HFC_inb_nodebug(hc, R_SYNC_STA); + if (temp != 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "lost frame sync\n", + __func__, hc->id); + hc->chan[hc->dslot].sync = 1; + } + break; + } + } + + if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) + hfcmulti_watchdog(hc); + + if (hc->leds) + hfcmulti_leds(hc); +} + +static void +ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech) +{ + struct dchannel *dch; + int ch; + int active; + u_char st_status, temp; + + /* state machine */ + for (ch = 0; ch <= 31; ch++) { + if (hc->chan[ch].dch) { + dch = hc->chan[ch].dch; + if (r_irq_statech & 1) { + HFC_outb_nodebug(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + /* undocumented: status changes during read */ + st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE); + while (st_status != (temp = + HFC_inb_nodebug(hc, A_ST_RD_STATE))) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: reread " + "STATE because %d!=%d\n", + __func__, temp, + st_status); + st_status = temp; /* repeat */ + } + + /* Speech Design TE-sync indication */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && + dch->dev.D.protocol == ISDN_P_TE_S0) { + if (st_status & V_FR_SYNC_ST) + hc->syncronized |= + (1 << hc->chan[ch].port); + else + hc->syncronized &= + ~(1 << hc->chan[ch].port); + } + dch->state = st_status & 0x0f; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + if (dch->state == active) { + HFC_outb_nodebug(hc, R_FIFO, + (ch << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, + R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + dch->tx_idx = 0; + } + schedule_event(dch, FLG_PHCHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: S/T newstate %x port %d\n", + __func__, dch->state, + hc->chan[ch].port); + } + r_irq_statech >>= 1; + } + } + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + plxsd_checksync(hc, 0); +} + +static void +fifo_irq(struct hfc_multi *hc, int block) +{ + int ch, j; + struct dchannel *dch; + struct bchannel *bch; + u_char r_irq_fifo_bl; + + r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block); + j = 0; + while (j < 8) { + ch = (block << 2) + (j >> 1); + dch = hc->chan[ch].dch; + bch = hc->chan[ch].bch; + if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) { + j += 2; + continue; + } + if (dch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &dch->Flags)) { + hfcmulti_tx(hc, ch); + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + if (bch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &bch->Flags)) { + hfcmulti_tx(hc, ch); + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + j++; + if (dch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &dch->Flags)) { + hfcmulti_rx(hc, ch); + } + if (bch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &bch->Flags)) { + hfcmulti_rx(hc, ch); + } + j++; + } +} + +#ifdef IRQ_DEBUG +int irqsem; +#endif +static irqreturn_t +hfcmulti_interrupt(int intno, void *dev_id) +{ +#ifdef IRQCOUNT_DEBUG + static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0, + iq5 = 0, iq6 = 0, iqcnt = 0; +#endif + static int count; + struct hfc_multi *hc = dev_id; + struct dchannel *dch; + u_char r_irq_statech, status, r_irq_misc, r_irq_oview; + int i; + u_short *plx_acc, wval; + u_char e1_syncsta, temp; + u_long flags; + + if (!hc) { + printk(KERN_ERR "HFC-multi: Spurious interrupt!\n"); + return IRQ_NONE; + } + + spin_lock(&hc->lock); + +#ifdef IRQ_DEBUG + if (irqsem) + printk(KERN_ERR "irq for card %d during irq from " + "card %d, this is no bug.\n", hc->id + 1, irqsem); + irqsem = hc->id + 1; +#endif + + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, flags); + plx_acc = (u_short *)(hc->plx_membase + PLX_INTCSR); + wval = readw(plx_acc); + spin_unlock_irqrestore(&plx_lock, flags); + if (!(wval & PLX_INTCSR_LINTI1_STATUS)) + goto irq_notforus; + } + + status = HFC_inb_nodebug(hc, R_STATUS); + r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH); +#ifdef IRQCOUNT_DEBUG + if (r_irq_statech) + iq1++; + if (status & V_DTMF_STA) + iq2++; + if (status & V_LOST_STA) + iq3++; + if (status & V_EXT_IRQSTA) + iq4++; + if (status & V_MISC_IRQSTA) + iq5++; + if (status & V_FR_IRQSTA) + iq6++; + if (iqcnt++ > 5000) { + printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n", + iq1, iq2, iq3, iq4, iq5, iq6); + iqcnt = 0; + } +#endif + if (!r_irq_statech && + !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | + V_MISC_IRQSTA | V_FR_IRQSTA))) { + /* irq is not for us */ + goto irq_notforus; + } + hc->irqcnt++; + if (r_irq_statech) { + if (hc->type != 1) + ph_state_irq(hc, r_irq_statech); + } + if (status & V_EXT_IRQSTA) + ; /* external IRQ */ + if (status & V_LOST_STA) { + /* LOST IRQ */ + HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */ + } + if (status & V_MISC_IRQSTA) { + /* misc IRQ */ + r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC); + if (r_irq_misc & V_STA_IRQ) { + if (hc->type == 1) { + /* state machine */ + dch = hc->chan[hc->dslot].dch; + e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && hc->e1_getclock) { + if (e1_syncsta & V_FR_SYNC_E1) + hc->syncronized = 1; + else + hc->syncronized = 0; + } + /* undocumented: status changes during read */ + dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA); + while (dch->state != (temp = + HFC_inb_nodebug(hc, R_E1_RD_STA))) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: reread " + "STATE because %d!=%d\n", + __func__, temp, + dch->state); + dch->state = temp; /* repeat */ + } + dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA) + & 0x7; + schedule_event(dch, FLG_PHCHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + plxsd_checksync(hc, 0); + } + } + if (r_irq_misc & V_TI_IRQ) + handle_timer_irq(hc); + + if (r_irq_misc & V_DTMF_IRQ) { + /* -> DTMF IRQ */ + hfcmulti_dtmf(hc); + } + /* TODO: REPLACE !!!! 125 us Interrupts are not acceptable */ + if (r_irq_misc & V_IRQ_PROC) { + /* IRQ every 125us */ + count++; + /* generate 1kHz signal */ + if (count == 8) { + if (hfc_interrupt) + hfc_interrupt(); + count = 0; + } + } + + } + if (status & V_FR_IRQSTA) { + /* FIFO IRQ */ + r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW); + for (i = 0; i < 8; i++) { + if (r_irq_oview & (1 << i)) + fifo_irq(hc, i); + } + } + +#ifdef IRQ_DEBUG + irqsem = 0; +#endif + spin_unlock(&hc->lock); + return IRQ_HANDLED; + +irq_notforus: +#ifdef IRQ_DEBUG + irqsem = 0; +#endif + spin_unlock(&hc->lock); + return IRQ_NONE; +} + + +/* + * timer callback for D-chan busy resolution. Currently no function + */ + +static void +hfcmulti_dbusy_timer(struct hfc_multi *hc) +{ +} + + +/* + * activate/deactivate hardware for selected channels and mode + * + * configure B-channel with the given protocol + * ch eqals to the HFC-channel (0-31) + * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31 + * for S/T, 1-31 for E1) + * the hdlc interrupts will be set/unset + */ +static int +mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, + int bank_tx, int slot_rx, int bank_rx) +{ + int flow_tx = 0, flow_rx = 0, routing = 0; + int oslot_tx, oslot_rx; + int conf; + + if (ch < 0 || ch > 31) + return EINVAL; + oslot_tx = hc->chan[ch].slot_tx; + oslot_rx = hc->chan[ch].slot_rx; + conf = hc->chan[ch].conf; + + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: card %d channel %d protocol %x slot old=%d new=%d " + "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n", + __func__, hc->id, ch, protocol, oslot_tx, slot_tx, + bank_tx, oslot_rx, slot_rx, bank_rx); + + if (oslot_tx >= 0 && slot_tx != oslot_tx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: remove from slot %d (TX)\n", + __func__, oslot_tx); + if (hc->slot_owner[oslot_tx<<1] == ch) { + HFC_outb(hc, R_SLOT, oslot_tx << 1); + HFC_outb(hc, A_SL_CFG, 0); + HFC_outb(hc, A_CONF, 0); + hc->slot_owner[oslot_tx<<1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: we are not owner of this tx slot " + "anymore, channel %d is.\n", + __func__, hc->slot_owner[oslot_tx<<1]); + } + } + + if (oslot_rx >= 0 && slot_rx != oslot_rx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: remove from slot %d (RX)\n", + __func__, oslot_rx); + if (hc->slot_owner[(oslot_rx << 1) | 1] == ch) { + HFC_outb(hc, R_SLOT, (oslot_rx << 1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, 0); + hc->slot_owner[(oslot_rx << 1) | 1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: we are not owner of this rx slot " + "anymore, channel %d is.\n", + __func__, + hc->slot_owner[(oslot_rx << 1) | 1]); + } + } + + if (slot_tx < 0) { + flow_tx = 0x80; /* FIFO->ST */ + /* disable pcm slot */ + hc->chan[ch].slot_tx = -1; + hc->chan[ch].bank_tx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_tx = 0x80; /* FIFO->ST */ + else + flow_tx = 0xc0; /* PCM->ST */ + /* put on slot */ + routing = bank_tx ? 0xc0 : 0x80; + if (conf >= 0 || bank_tx > 1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put channel %d to slot %d bank" + " %d flow %02x routing %02x conf %d (TX)\n", + __func__, ch, slot_tx, bank_tx, + flow_tx, routing, conf); + HFC_outb(hc, R_SLOT, slot_tx << 1); + HFC_outb(hc, A_SL_CFG, (ch<<1) | routing); + HFC_outb(hc, A_CONF, (conf < 0) ? 0 : (conf | V_CONF_SL)); + hc->slot_owner[slot_tx << 1] = ch; + hc->chan[ch].slot_tx = slot_tx; + hc->chan[ch].bank_tx = bank_tx; + } + if (slot_rx < 0) { + /* disable pcm slot */ + flow_rx = 0x80; /* ST->FIFO */ + hc->chan[ch].slot_rx = -1; + hc->chan[ch].bank_rx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_rx = 0x80; /* ST->FIFO */ + else + flow_rx = 0xc0; /* ST->(FIFO,PCM) */ + /* put on slot */ + routing = bank_rx?0x80:0xc0; /* reversed */ + if (conf >= 0 || bank_rx > 1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put channel %d to slot %d bank" + " %d flow %02x routing %02x conf %d (RX)\n", + __func__, ch, slot_rx, bank_rx, + flow_rx, routing, conf); + HFC_outb(hc, R_SLOT, (slot_rx<<1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, (ch<<1) | V_CH_DIR | routing); + hc->slot_owner[(slot_rx<<1)|1] = ch; + hc->chan[ch].slot_rx = slot_rx; + hc->chan[ch].bank_rx = bank_rx; + } + + switch (protocol) { + case (ISDN_P_NONE): + /* disable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* disable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->chan[ch].bch && hc->type != 1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] &= + ((ch & 0x3) == 0)? ~V_B1_EN: ~V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + if (hc->chan[ch].bch) { + test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); + test_and_clear_bit(FLG_TRANSPARENT, + &hc->chan[ch].bch->Flags); + } + break; + case (ISDN_P_B_RAW): /* B-channel */ + + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) { + + printk(KERN_DEBUG + "Setting B-channel %d to echo cancelable " + "state on PCM slot %d\n", ch, + ((ch / 4) * 8) + ((ch % 4) * 4) + 1); + printk(KERN_DEBUG + "Enabling pass through for channel\n"); + vpm_out(hc, ch, ((ch / 4) * 8) + + ((ch % 4) * 4) + 1, 0x01); + /* rx path */ + /* S/T -> PCM */ + HFC_outb(hc, R_FIFO, (ch << 1)); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + + ((ch % 4) * 4) + 1) << 1); + HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1)); + + /* PCM -> FIFO */ + HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + + ((ch % 4) * 4) + 1) << 1) | 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1); + + /* tx path */ + /* PCM -> S/T */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + + ((ch % 4) * 4)) << 1) | 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1); + + /* FIFO -> PCM */ + HFC_outb(hc, R_FIFO, 0x20 | (ch << 1)); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* tx silence */ + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + + ((ch % 4) * 4)) << 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1)); + } else { + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* tx silence */ + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | V_HDLC_TRP); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + if (hc->type != 1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= + ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + if (hc->chan[ch].bch) + test_and_set_bit(FLG_TRANSPARENT, + &hc->chan[ch].bch->Flags); + break; + case (ISDN_P_B_HDLC): /* B-channel */ + case (ISDN_P_TE_S0): /* D-channel */ + case (ISDN_P_NT_S0): + case (ISDN_P_TE_E1): + case (ISDN_P_NT_E1): + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch<<1); + HFC_wait(hc); + if (hc->type == 1 || hc->chan[ch].bch) { + /* E1 or B-channel */ + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04); + HFC_outb(hc, A_SUBCH_CFG, 0); + } else { + /* D-Channel without HDLC fill flags */ + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 2); + } + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch<<1)|1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); + if (hc->type == 1 || hc->chan[ch].bch) + HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */ + else + HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */ + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->chan[ch].bch) { + test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); + if (hc->type != 1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= + ((ch&0x3) == 0) ? V_B1_EN : V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + } + break; + default: + printk(KERN_DEBUG "%s: protocol not known %x\n", + __func__, protocol); + hc->chan[ch].protocol = ISDN_P_NONE; + return -ENOPROTOOPT; + } + hc->chan[ch].protocol = protocol; + return 0; +} + + +/* + * connect/disconnect PCM + */ + +static void +hfcmulti_pcm(struct hfc_multi *hc, int ch, int slot_tx, int bank_tx, + int slot_rx, int bank_rx) +{ + if (slot_rx < 0 || slot_rx < 0 || bank_tx < 0 || bank_rx < 0) { + /* disable PCM */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0); + return; + } + + /* enable pcm */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx, + slot_rx, bank_rx); +} + +/* + * set/disable conference + */ + +static void +hfcmulti_conf(struct hfc_multi *hc, int ch, int num) +{ + if (num >= 0 && num <= 7) + hc->chan[ch].conf = num; + else + hc->chan[ch].conf = -1; + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx, + hc->chan[ch].bank_tx, hc->chan[ch].slot_rx, + hc->chan[ch].bank_rx); +} + + +/* + * set/disable sample loop + */ + +/* NOTE: this function is experimental and therefore disabled */ + +/* + * Layer 1 callback function + */ +static int +hfcm_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfc_multi *hc = dch->hw; + u_long flags; + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + break; + case HW_RESET_REQ: + /* start activation */ + spin_lock_irqsave(&hc->lock, flags); + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_RESET_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); + HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT*3)); + /* activate */ + } + spin_unlock_irqrestore(&hc->lock, flags); + l1_event(dch->l1, HW_POWERUP_IND); + break; + case HW_DEACT_REQ: + /* start deactivation */ + spin_lock_irqsave(&hc->lock, flags); + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_DEACT_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2); + /* deactivate */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[dch->slot].port); + plxsd_checksync(hc, 0); + } + } + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case HW_POWERUP_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_POWERUP_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */ + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -1; + } + return 0; +} + +/* + * Layer2 -> Layer 1 Transfer + */ + +static int +handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_multi *hc = dch->hw; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (skb->len < 1) + break; + spin_lock_irqsave(&hc->lock, flags); + ret = dchannel_senddata(dch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcmulti_tx(hc, dch->slot); + ret = 0; + /* start fifo */ + HFC_outb(hc, R_FIFO, 0); + HFC_wait(hc); + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + spin_lock_irqsave(&hc->lock, flags); + ret = 0; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_ACTIVATE port %d (0..%d)\n", + __func__, hc->chan[dch->slot].port, + hc->ports-1); + /* start activation */ + if (hc->type == 1) { + ph_state_change(dch); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 report state %x \n", + __func__, dch->state); + } else { + HFC_outb(hc, R_ST_SEL, + hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); + /* G1 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 1); + HFC_outb(hc, A_ST_WR_STATE, 1 | + (V_ST_ACT*3)); /* activate */ + dch->state = 1; + } + spin_unlock_irqrestore(&hc->lock, flags); + } else + ret = l1_event(dch->l1, hh->prim); + break; + case PH_DEACTIVATE_REQ: + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + spin_lock_irqsave(&hc->lock, flags); + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_DEACTIVATE port %d (0..%d)\n", + __func__, hc->chan[dch->slot].port, + hc->ports-1); + /* start deactivation */ + if (hc->type == 1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_DEACTIVATE no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, + hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); + /* deactivate */ + dch->state = 1; + } + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags)) + del_timer(&dch->timer); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + } else + ret = l1_event(dch->l1, hh->prim); + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfc_multi *hc = bch->hw; + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + if (bch->tx_skb) { + dev_kfree_skb(bch->tx_skb); + bch->tx_skb = NULL; + } + bch->tx_idx = 0; + if (bch->rx_skb) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + } + hc->chan[bch->slot].coeff_count = 0; + test_and_clear_bit(FLG_ACTIVE, &bch->Flags); + test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); + hc->chan[bch->slot].rx_off = 0; + hc->chan[bch->slot].conf = -1; + mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0); + spin_unlock_irqrestore(&hc->lock, flags); +} + +static int +handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_multi *hc = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (!skb->len) + break; + spin_lock_irqsave(&hc->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcmulti_tx(hc, bch->slot); + ret = 0; + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) { + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", + __func__, bch->slot); + spin_lock_irqsave(&hc->lock, flags); + /* activate B-channel if not already activated */ + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + hc->chan[bch->slot].txpending = 0; + ret = mode_hfcmulti(hc, bch->slot, + ch->protocol, + hc->chan[bch->slot].slot_tx, + hc->chan[bch->slot].bank_tx, + hc->chan[bch->slot].slot_rx, + hc->chan[bch->slot].bank_rx); + if (!ret) { + if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf + && test_bit(HFC_CHIP_DTMF, &hc->chip)) { + /* start decoder */ + hc->dtmf = 1; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG + "%s: start dtmf decoder\n", + __func__); + HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | + V_RST_DTMF); + } + } + } else + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL, + GFP_KERNEL); + break; + case PH_CONTROL_REQ: + spin_lock_irqsave(&hc->lock, flags); + switch (hh->id) { + case HFC_SPL_LOOP_ON: /* set sample loop */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HFC_SPL_LOOP_ON (len = %d)\n", + __func__, skb->len); + ret = 0; + break; + case HFC_SPL_LOOP_OFF: /* set silence */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n", + __func__); + ret = 0; + break; + default: + printk(KERN_ERR + "%s: unknown PH_CONTROL_REQ info %x\n", + __func__, hh->id); + ret = -EINVAL; + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); /* locked there */ + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL, + GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * bchannel control function + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + struct dsp_features *features = + (struct dsp_features *)(*((u_long *)&cq->p1)); + struct hfc_multi *hc = bch->hw; + int slot_tx; + int bank_tx; + int slot_rx; + int bank_rx; + int num; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP + | MISDN_CTRL_RX_OFF; + break; + case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */ + hc->chan[bch->slot].rx_off = !!cq->p1; + if (!hc->chan[bch->slot].rx_off) { + /* reset fifo on rx on */ + HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + } + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n", + __func__, bch->nr, hc->chan[bch->slot].rx_off); + break; + case MISDN_CTRL_HW_FEATURES: /* fill features structure */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_FEATURE request\n", + __func__); + /* create confirm */ + features->hfc_id = hc->id; + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) + features->hfc_dtmf = 1; + features->hfc_loops = 0; + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + features->hfc_echocanhw = 1; + } else { + features->pcm_id = hc->pcm; + features->pcm_slots = hc->slots; + features->pcm_banks = 2; + } + break; + case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */ + slot_tx = cq->p1 & 0xff; + bank_tx = cq->p1 >> 8; + slot_rx = cq->p2 & 0xff; + bank_rx = cq->p2 >> 8; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HFC_PCM_CONN slot %d bank %d (TX) " + "slot %d bank %d (RX)\n", + __func__, slot_tx, bank_tx, + slot_rx, bank_rx); + if (slot_tx < hc->slots && bank_tx <= 2 && + slot_rx < hc->slots && bank_rx <= 2) + hfcmulti_pcm(hc, bch->slot, + slot_tx, bank_tx, slot_rx, bank_rx); + else { + printk(KERN_WARNING + "%s: HFC_PCM_CONN slot %d bank %d (TX) " + "slot %d bank %d (RX) out of range\n", + __func__, slot_tx, bank_tx, + slot_rx, bank_rx); + ret = -EINVAL; + } + break; + case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_PCM_DISC\n", + __func__); + hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0); + break; + case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */ + num = cq->p1 & 0xff; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n", + __func__, num); + if (num <= 7) + hfcmulti_conf(hc, bch->slot, num); + else { + printk(KERN_WARNING + "%s: HW_CONF_JOIN conf %d out of range\n", + __func__, num); + ret = -EINVAL; + } + break; + case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__); + hfcmulti_conf(hc, bch->slot, -1); + break; + case MISDN_CTRL_HFC_ECHOCAN_ON: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__); + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + vpm_echocan_on(hc, bch->slot, cq->p1); + else + ret = -EINVAL; + break; + + case MISDN_CTRL_HFC_ECHOCAN_OFF: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n", + __func__); + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + vpm_echocan_off(hc, bch->slot); + else + ret = -EINVAL; + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_multi *hc = bch->hw; + int err = -EINVAL; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + if (test_bit(FLG_ACTIVE, &bch->Flags)) + deactivate_bchannel(bch); /* locked there */ + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + err = 0; + break; + case CONTROL_CHANNEL: + spin_lock_irqsave(&hc->lock, flags); + err = channel_bctrl(bch, arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return err; +} + +/* + * handle D-channel events + * + * handle state change event + */ +static void +ph_state_change(struct dchannel *dch) +{ + struct hfc_multi *hc = dch->hw; + int ch, i; + + if (!dch) { + printk(KERN_WARNING "%s: ERROR given dch is NULL\n", + __func__); + return; + } + ch = dch->slot; + + if (hc->type == 1) { + if (dch->dev.D.protocol == ISDN_P_TE_E1) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 TE (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 NT (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + } + switch (dch->state) { + case (1): + if (hc->e1_state != 1) { + for (i = 1; i <= 31; i++) { + /* reset fifos on e1 activation */ + HFC_outb_nodebug(hc, R_FIFO, (i << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, + R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + } + } + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + + default: + if (hc->e1_state != 1) + return; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + } + hc->e1_state = dch->state; + } else { + if (dch->dev.D.protocol == ISDN_P_TE_S0) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: S/T TE newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case (0): + l1_event(dch->l1, HW_RESET_IND); + break; + case (3): + l1_event(dch->l1, HW_DEACT_IND); + break; + case (5): + case (8): + l1_event(dch->l1, ANYSIGNAL); + break; + case (6): + l1_event(dch->l1, INFO2); + break; + case (7): + l1_event(dch->l1, INFO4_P8); + break; + } + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: S/T NT newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case (2): + if (hc->chan[ch].nt_timer == 0) { + hc->chan[ch].nt_timer = -1; + HFC_outb(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, 4 | + V_ST_LD_STA); /* G4 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 4); + dch->state = 4; + } else { + /* one extra count for the next event */ + hc->chan[ch].nt_timer = + nt_t1_count[poll_timer] + 1; + HFC_outb(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + /* allow G2 -> G3 transition */ + HFC_outb(hc, A_ST_WR_STATE, 2 | + V_SET_G2_G3); + } + break; + case (1): + hc->chan[ch].nt_timer = -1; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + case (4): + hc->chan[ch].nt_timer = -1; + break; + case (3): + hc->chan[ch].nt_timer = -1; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + } + } + } +} + +/* + * called for card mode init message + */ + +static void +hfcmulti_initmode(struct dchannel *dch) +{ + struct hfc_multi *hc = dch->hw; + u_char a_st_wr_state, r_e1_wr_sta; + int i, pt; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + if (hc->type == 1) { + hc->chan[hc->dslot].slot_tx = -1; + hc->chan[hc->dslot].slot_rx = -1; + hc->chan[hc->dslot].conf = -1; + if (hc->dslot) { + mode_hfcmulti(hc, hc->dslot, dch->dev.D.protocol, + -1, 0, -1, 0); + dch->timer.function = (void *) hfcmulti_dbusy_timer; + dch->timer.data = (long) dch; + init_timer(&dch->timer); + } + for (i = 1; i <= 31; i++) { + if (i == hc->dslot) + continue; + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0); + } + /* E1 */ + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) { + HFC_outb(hc, R_LOS0, 255); /* 2 ms */ + HFC_outb(hc, R_LOS1, 255); /* 512 ms */ + } + if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dslot].cfg)) { + HFC_outb(hc, R_RX0, 0); + hc->hw.r_tx0 = 0 | V_OUT_EN; + } else { + HFC_outb(hc, R_RX0, 1); + hc->hw.r_tx0 = 1 | V_OUT_EN; + } + hc->hw.r_tx1 = V_ATX | V_NTRI; + HFC_outb(hc, R_TX0, hc->hw.r_tx0); + HFC_outb(hc, R_TX1, hc->hw.r_tx1); + HFC_outb(hc, R_TX_FR0, 0x00); + HFC_outb(hc, R_TX_FR1, 0xf8); + + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg)) + HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); + + HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); + + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg)) + HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); + + if (dch->dev.D.protocol == ISDN_P_NT_E1) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is NT-mode\n", + __func__); + r_e1_wr_sta = 0; /* G0 */ + hc->e1_getclock = 0; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is TE-mode\n", + __func__); + r_e1_wr_sta = 0; /* F0 */ + hc->e1_getclock = 1; + } + if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) + HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); + else + HFC_outb(hc, R_SYNC_OUT, 0); + if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip)) + hc->e1_getclock = 1; + if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip)) + hc->e1_getclock = 0; + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + /* SLAVE (clock master) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: E1 port is clock master " + "(clock from PCM)\n", __func__); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC); + } else { + if (hc->e1_getclock) { + /* MASTER (clock slave) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: E1 port is clock slave " + "(clock to PCM)\n", __func__); + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } else { + /* MASTER (clock master) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is " + "clock master " + "(clock from QUARTZ)\n", + __func__); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | + V_PCM_SYNC | V_JATT_OFF); + HFC_outb(hc, R_SYNC_OUT, 0); + } + } + HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */ + HFC_outb(hc, R_PWM_MD, V_PWM0_MD); + HFC_outb(hc, R_PWM0, 0x50); + HFC_outb(hc, R_PWM1, 0xff); + /* state machine setup */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized = 0; + plxsd_checksync(hc, 0); + } + } else { + i = dch->slot; + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0); + dch->timer.function = (void *)hfcmulti_dbusy_timer; + dch->timer.data = (long) dch; + init_timer(&dch->timer); + hc->chan[i - 2].slot_tx = -1; + hc->chan[i - 2].slot_rx = -1; + hc->chan[i - 2].conf = -1; + mode_hfcmulti(hc, i - 2, ISDN_P_NONE, -1, 0, -1, 0); + hc->chan[i - 1].slot_tx = -1; + hc->chan[i - 1].slot_rx = -1; + hc->chan[i - 1].conf = -1; + mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0); + /* ST */ + pt = hc->chan[i].port; + /* select interface */ + HFC_outb(hc, R_ST_SEL, pt); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + if (dch->dev.D.protocol == ISDN_P_NT_S0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: ST port %d is NT-mode\n", + __func__, pt); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt); + a_st_wr_state = 1; /* G1 */ + hc->hw.a_st_ctrl0[pt] = V_ST_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: ST port %d is TE-mode\n", + __func__, pt); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te); + a_st_wr_state = 2; /* F2 */ + hc->hw.a_st_ctrl0[pt] = 0; + } + if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) + hc->hw.a_st_ctrl0[pt] |= V_TX_LI; + /* line setup */ + HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[pt]); + /* disable E-channel */ + if ((dch->dev.D.protocol == ISDN_P_NT_S0) || + test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg)) + HFC_outb(hc, A_ST_CTRL1, V_E_IGNO); + else + HFC_outb(hc, A_ST_CTRL1, 0); + /* enable B-channel receive */ + HFC_outb(hc, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN); + /* state machine setup */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state); + hc->hw.r_sci_msk |= 1 << pt; + /* state machine interrupts */ + HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk); + /* unset sync on port */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[dch->slot].port); + plxsd_checksync(hc, 0); + } + } + if (debug & DEBUG_HFCMULTI_INIT) + printk("%s: done\n", __func__); +} + + +static int +open_dchannel(struct hfc_multi *hc, struct dchannel *dch, + struct channel_req *rq) +{ + int err = 0; + u_long flags; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, + dch->dev.id, __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if ((dch->dev.D.protocol != ISDN_P_NONE) && + (dch->dev.D.protocol != rq->protocol)) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_WARNING "%s: change protocol %x to %x\n", + __func__, dch->dev.D.protocol, rq->protocol); + } + if ((dch->dev.D.protocol == ISDN_P_TE_S0) + && (rq->protocol != ISDN_P_TE_S0)) + l1_event(dch->l1, CLOSE_CHANNEL); + if (dch->dev.D.protocol != rq->protocol) { + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(dch, hfcm_l1callback); + if (err) + return err; + } + dch->dev.D.protocol = rq->protocol; + spin_lock_irqsave(&hc->lock, flags); + hfcmulti_initmode(dch); + spin_unlock_irqrestore(&hc->lock, flags); + } + + if (((rq->protocol == ISDN_P_NT_S0) && (dch->state == 3)) || + ((rq->protocol == ISDN_P_TE_S0) && (dch->state == 7)) || + ((rq->protocol == ISDN_P_NT_E1) && (dch->state == 1)) || + ((rq->protocol == ISDN_P_TE_E1) && (dch->state == 1))) { + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + } + rq->ch = &dch->dev.D; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +static int +open_bchannel(struct hfc_multi *hc, struct dchannel *dch, + struct channel_req *rq) +{ + struct bchannel *bch; + int ch; + + if (!test_bit(rq->adr.channel, &dch->dev.channelmap[0])) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if (hc->type == 1) + ch = rq->adr.channel; + else + ch = (rq->adr.channel - 1) + (dch->slot - 2); + bch = hc->chan[ch].bch; + if (!bch) { + printk(KERN_ERR "%s:internal error ch %d has no bch\n", + __func__, ch); + return -EINVAL; + } + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + hc->chan[ch].rx_off = 0; + rq->ch = &bch->ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +/* + * device control function + */ +static int +channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = 0; + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_multi *hc = dch->hw; + struct channel_req *rq; + int err = 0; + u_long flags; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + switch (rq->protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + if (hc->type == 1) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); /* locked there */ + break; + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + if (hc->type != 1) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); /* locked there */ + break; + default: + spin_lock_irqsave(&hc->lock, flags); + err = open_bchannel(hc, dch, rq); + spin_unlock_irqrestore(&hc->lock, flags); + } + break; + case CLOSE_CHANNEL: + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, dch->dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + spin_lock_irqsave(&hc->lock, flags); + err = channel_dctrl(dch, arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + err = -EINVAL; + } + return err; +} + +/* + * initialize the card + */ + +/* + * start timer irq, wait some time and check if we have interrupts. + * if not, reset chip and try again. + */ +static int +init_card(struct hfc_multi *hc) +{ + int err = -EIO; + u_long flags; + u_short *plx_acc; + u_long plx_flags; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + spin_lock_irqsave(&hc->lock, flags); + /* set interrupts but leave global interrupt disabled */ + hc->hw.r_irq_ctrl = V_FIFO_IRQ; + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + + if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, IRQF_SHARED, + "HFC-multi", hc)) { + printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", + hc->pci_dev->irq); + return -EIO; + } + hc->irq = hc->pci_dev->irq; + + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR); + writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE), + plx_acc); /* enable PCI & LINT1 irq */ + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", + __func__, hc->irq, hc->irqcnt); + err = init_chip(hc); + if (err) + goto error; + /* + * Finally enable IRQ output + * this is only allowed, if an IRQ routine is allready + * established for this HFC, so don't do that earlier + */ + spin_lock_irqsave(&hc->lock, flags); + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + /* printk(KERN_DEBUG "no master irq set!!!\n"); */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100*HZ)/1000); /* Timeout 100ms */ + /* turn IRQ off until chip is completely initialized */ + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", + __func__, hc->irq, hc->irqcnt); + if (hc->irqcnt) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); + + return 0; + } + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_INFO "ignoring missing interrupts\n"); + return 0; + } + + printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n", + hc->irq); + + err = -EIO; + +error: + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR); + writew(0x00, plx_acc); /*disable IRQs*/ + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: free irq %d\n", __func__, hc->irq); + if (hc->irq) { + free_irq(hc->irq, hc); + hc->irq = 0; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err); + return err; +} + +/* + * find pci device and set it up + */ + +static int +setup_pci(struct hfc_multi *hc, struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + + printk(KERN_INFO + "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", + m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); + + hc->pci_dev = pdev; + if (m->clock2) + test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); + + if (ent->device == 0xB410) { + test_and_set_bit(HFC_CHIP_B410P, &hc->chip); + test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); + test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + hc->slots = 32; + } + + if (hc->pci_dev->irq <= 0) { + printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n"); + return -EIO; + } + if (pci_enable_device(hc->pci_dev)) { + printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n"); + return -EIO; + } + hc->leds = m->leds; + hc->ledstate = 0xAFFEAFFE; + hc->opticalsupport = m->opticalsupport; + + /* set memory access methods */ + if (m->io_mode) /* use mode from card config */ + hc->io_mode = m->io_mode; + switch (hc->io_mode) { + case HFC_IO_MODE_PLXSD: + test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip); + hc->slots = 128; /* required */ + /* fall through */ + case HFC_IO_MODE_PCIMEM: + hc->HFC_outb = HFC_outb_pcimem; + hc->HFC_inb = HFC_inb_pcimem; + hc->HFC_inw = HFC_inw_pcimem; + hc->HFC_wait = HFC_wait_pcimem; + hc->read_fifo = read_fifo_pcimem; + hc->write_fifo = write_fifo_pcimem; + break; + case HFC_IO_MODE_REGIO: + hc->HFC_outb = HFC_outb_regio; + hc->HFC_inb = HFC_inb_regio; + hc->HFC_inw = HFC_inw_regio; + hc->HFC_wait = HFC_wait_regio; + hc->read_fifo = read_fifo_regio; + hc->write_fifo = write_fifo_regio; + break; + default: + printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + hc->HFC_outb_nodebug = hc->HFC_outb; + hc->HFC_inb_nodebug = hc->HFC_inb; + hc->HFC_inw_nodebug = hc->HFC_inw; + hc->HFC_wait_nodebug = hc->HFC_wait; +#ifdef HFC_REGISTER_DEBUG + hc->HFC_outb = HFC_outb_debug; + hc->HFC_inb = HFC_inb_debug; + hc->HFC_inw = HFC_inw_debug; + hc->HFC_wait = HFC_wait_debug; +#endif + hc->pci_iobase = 0; + hc->pci_membase = NULL; + hc->plx_membase = NULL; + + switch (hc->io_mode) { + case HFC_IO_MODE_PLXSD: + hc->plx_origmembase = hc->pci_dev->resource[0].start; + /* MEMBASE 1 is PLX PCI Bridge */ + + if (!hc->plx_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI PLX bridge found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->plx_membase = ioremap(hc->plx_origmembase, 0x80); + if (!hc->plx_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap plx address space. " + "(internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + printk(KERN_INFO + "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n", + (u_long)hc->plx_membase, hc->plx_origmembase); + + hc->pci_origmembase = hc->pci_dev->resource[2].start; + /* MEMBASE 1 is PLX PCI Bridge */ + if (!hc->pci_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->pci_membase = ioremap(hc->pci_origmembase, 0x400); + if (!hc->pci_membase) { + printk(KERN_WARNING "HFC-multi: failed to remap io " + "address space. (internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + printk(KERN_INFO + "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d " + "leds-type %d\n", + hc->id, (u_long)hc->pci_membase, hc->pci_origmembase, + hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); + break; + case HFC_IO_MODE_PCIMEM: + hc->pci_origmembase = hc->pci_dev->resource[1].start; + if (!hc->pci_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->pci_membase = ioremap(hc->pci_origmembase, 256); + if (!hc->pci_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap io address space. " + "(internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d " + "HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase, + hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); + break; + case HFC_IO_MODE_REGIO: + hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start; + if (!hc->pci_iobase) { + printk(KERN_WARNING + "HFC-multi: No IO for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + if (!request_region(hc->pci_iobase, 8, "hfcmulti")) { + printk(KERN_WARNING "HFC-multi: failed to request " + "address space at 0x%08lx (internal error)\n", + hc->pci_iobase); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + printk(KERN_INFO + "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n", + m->vendor_name, m->card_name, (u_int) hc->pci_iobase, + hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO); + break; + default: + printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + pci_set_drvdata(hc->pci_dev, hc); + + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + return 0; +} + + +/* + * remove port + */ + +static void +release_port(struct hfc_multi *hc, struct dchannel *dch) +{ + int pt, ci, i = 0; + u_long flags; + struct bchannel *pb; + + ci = dch->slot; + pt = hc->chan[ci].port; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered for port %d\n", + __func__, pt + 1); + + if (pt >= hc->ports) { + printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", + __func__, pt + 1); + return; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: releasing port=%d\n", + __func__, pt + 1); + + if (dch->dev.D.protocol == ISDN_P_TE_S0) + l1_event(dch->l1, CLOSE_CHANNEL); + + hc->chan[ci].dch = NULL; + + if (hc->created[pt]) { + hc->created[pt] = 0; + mISDN_unregister_device(&dch->dev); + } + + spin_lock_irqsave(&hc->lock, flags); + + if (dch->timer.function) { + del_timer(&dch->timer); + dch->timer.function = NULL; + } + + if (hc->type == 1) { /* E1 */ + /* remove sync */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized = 0; + plxsd_checksync(hc, 1); + } + /* free channels */ + for (i = 0; i <= 31; i++) { + if (hc->chan[i].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[i].port+1, i); + pb = hc->chan[i].bch; + hc->chan[i].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[i].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + } + } else { + /* remove sync */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[ci].port); + plxsd_checksync(hc, 1); + } + /* free channels */ + if (hc->chan[ci - 2].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[ci - 2].port+1, + ci - 2); + pb = hc->chan[ci - 2].bch; + hc->chan[ci - 2].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[ci - 2].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + if (hc->chan[ci - 1].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[ci - 1].port+1, + ci - 1); + pb = hc->chan[ci - 1].bch; + hc->chan[ci - 1].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[ci - 1].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + } + + spin_unlock_irqrestore(&hc->lock, flags); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free port %d channel D\n", __func__, pt); + mISDN_freedchannel(dch); + kfree(dch); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done!\n", __func__); +} + +static void +release_card(struct hfc_multi *hc) +{ + u_long flags; + int ch; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: release card (%d) entered\n", + __func__, hc->id); + + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + + udelay(1000); + + /* dimm leds */ + if (hc->leds) + hfcmulti_leds(hc); + + /* disable D-channels & B-channels */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: disable all channels (d and b)\n", + __func__); + for (ch = 0; ch <= 31; ch++) { + if (hc->chan[ch].dch) + release_port(hc, hc->chan[ch].dch); + } + + /* release hardware & irq */ + if (hc->irq) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: free irq %d\n", + __func__, hc->irq); + free_irq(hc->irq, hc); + hc->irq = 0; + + } + release_io_hfcmulti(hc); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: remove instance from list\n", + __func__); + list_del(&hc->list); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: delete instance\n", __func__); + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_WARNING "%s: card successfully removed\n", + __func__); +} + +static int +init_e1_port(struct hfc_multi *hc, struct hm_map *m) +{ + struct dchannel *dch; + struct bchannel *bch; + int ch, ret = 0; + char name[MISDN_MAX_IDLEN]; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); + dch->hw = hc; + dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; + dch->dev.nrbchan = (hc->dslot)?30:31; + dch->slot = hc->dslot; + hc->chan[hc->dslot].dch = dch; + hc->chan[hc->dslot].port = 0; + hc->chan[hc->dslot].nt_timer = -1; + for (ch = 1; ch <= 31; ch++) { + if (ch == hc->dslot) /* skip dchannel */ + continue; + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL); + if (!hc->chan[ch].coeff) { + printk(KERN_ERR "%s: no memory for coeffs\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + bch->nr = ch; + bch->slot = ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = hfcm_bctrl; + bch->ch.nr = ch; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[ch].bch = bch; + hc->chan[ch].port = 0; + test_and_set_bit(bch->nr, &dch->dev.channelmap[0]); + } + /* set optical line type */ + if (port[Port_cnt] & 0x001) { + if (!m->opticalsupport) { + printk(KERN_INFO + "This board has no optical " + "support\n"); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set optical " + "interfacs: card(%d) " + "port(%d)\n", + __func__, + HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_OPTICAL, + &hc->chan[hc->dslot].cfg); + } + } + /* set LOS report */ + if (port[Port_cnt] & 0x004) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT set " + "LOS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_LOS, + &hc->chan[hc->dslot].cfg); + } + /* set AIS report */ + if (port[Port_cnt] & 0x008) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT set " + "AIS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_AIS, + &hc->chan[hc->dslot].cfg); + } + /* set SLIP report */ + if (port[Port_cnt] & 0x010) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set SLIP report: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_SLIP, + &hc->chan[hc->dslot].cfg); + } + /* set RDI report */ + if (port[Port_cnt] & 0x020) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set RDI report: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_RDI, + &hc->chan[hc->dslot].cfg); + } + /* set CRC-4 Mode */ + if (!(port[Port_cnt] & 0x100)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn on CRC4 report:" + " card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_CRC4, + &hc->chan[hc->dslot].cfg); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn off CRC4" + " report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + } + /* set forced clock */ + if (port[Port_cnt] & 0x0200) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT force getting clock from " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip); + } else + if (port[Port_cnt] & 0x0400) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT force putting clock to " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip); + } + /* set JATT PLL */ + if (port[Port_cnt] & 0x0800) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT disable JATT PLL on " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip); + } + /* set elastic jitter buffer */ + if (port[Port_cnt] & 0x3000) { + hc->chan[hc->dslot].jitter = (port[Port_cnt]>>12) & 0x3; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set elastic " + "buffer to %d: card(%d) port(%d)\n", + __func__, hc->chan[hc->dslot].jitter, + HFC_cnt + 1, 1); + } else + hc->chan[hc->dslot].jitter = 2; /* default */ + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); + ret = mISDN_register_device(&dch->dev, name); + if (ret) + goto free_chan; + hc->created[0] = 1; + return ret; +free_chan: + release_port(hc, dch); + return ret; +} + +static int +init_multi_port(struct hfc_multi *hc, int pt) +{ + struct dchannel *dch; + struct bchannel *bch; + int ch, i, ret = 0; + char name[MISDN_MAX_IDLEN]; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); + dch->hw = hc; + dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; + dch->dev.nrbchan = 2; + i = pt << 2; + dch->slot = i + 2; + hc->chan[i + 2].dch = dch; + hc->chan[i + 2].port = pt; + hc->chan[i + 2].nt_timer = -1; + for (ch = 0; ch < dch->dev.nrbchan; ch++) { + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL); + if (!hc->chan[i + ch].coeff) { + printk(KERN_ERR "%s: no memory for coeffs\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + bch->nr = ch + 1; + bch->slot = i + ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = hfcm_bctrl; + bch->ch.nr = ch + 1; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[i + ch].bch = bch; + hc->chan[i + ch].port = pt; + test_and_set_bit(bch->nr, &dch->dev.channelmap[0]); + } + /* set master clock */ + if (port[Port_cnt] & 0x001) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL set master clock: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + printk(KERN_ERR "Error: Master clock " + "for port(%d) of card(%d) is only" + " possible with TE-mode\n", + pt + 1, HFC_cnt + 1); + ret = -EINVAL; + goto free_chan; + } + if (hc->masterclk >= 0) { + printk(KERN_ERR "Error: Master clock " + "for port(%d) of card(%d) already " + "defined for port(%d)\n", + pt + 1, HFC_cnt + 1, hc->masterclk+1); + ret = -EINVAL; + goto free_chan; + } + hc->masterclk = pt; + } + /* set transmitter line to non capacitive */ + if (port[Port_cnt] & 0x002) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL set non capacitive " + "transmitter: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + test_and_set_bit(HFC_CFG_NONCAP_TX, + &hc->chan[i + 2].cfg); + } + /* disable E-channel */ + if (port[Port_cnt] & 0x004) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL disable E-channel: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + test_and_set_bit(HFC_CFG_DIS_ECHANNEL, + &hc->chan[i + 2].cfg); + } + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d", + hc->type, HFC_cnt + 1, pt + 1); + ret = mISDN_register_device(&dch->dev, name); + if (ret) + goto free_chan; + hc->created[pt] = 1; + return ret; +free_chan: + release_port(hc, dch); + return ret; +} + +static int +hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + int ret_err = 0; + int pt; + struct hfc_multi *hc; + u_long flags; + u_char dips = 0, pmj = 0; /* dip settings, port mode Jumpers */ + + if (HFC_cnt >= MAX_CARDS) { + printk(KERN_ERR "too many cards (max=%d).\n", + MAX_CARDS); + return -EINVAL; + } + if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) { + printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but " + "type[%d] %d was supplied as module parameter\n", + m->vendor_name, m->card_name, m->type, HFC_cnt, + type[HFC_cnt] & 0xff); + printk(KERN_WARNING "HFC-MULTI: Load module without parameters " + "first, to see cards and their types."); + return -EINVAL; + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n", + __func__, m->vendor_name, m->card_name, m->type, + type[HFC_cnt]); + + /* allocate card+fifo structure */ + hc = kzalloc(sizeof(struct hfc_multi), GFP_KERNEL); + if (!hc) { + printk(KERN_ERR "No kmem for HFC-Multi card\n"); + return -ENOMEM; + } + spin_lock_init(&hc->lock); + hc->mtyp = m; + hc->type = m->type; + hc->ports = m->ports; + hc->id = HFC_cnt; + hc->pcm = pcm[HFC_cnt]; + hc->io_mode = iomode[HFC_cnt]; + if (dslot[HFC_cnt] < 0) { + hc->dslot = 0; + printk(KERN_INFO "HFC-E1 card has disabled D-channel, but " + "31 B-channels\n"); + } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32) { + hc->dslot = dslot[HFC_cnt]; + printk(KERN_INFO "HFC-E1 card has alternating D-channel on " + "time slot %d\n", dslot[HFC_cnt]); + } else + hc->dslot = 16; + + /* set chip specific features */ + hc->masterclk = -1; + if (type[HFC_cnt] & 0x100) { + test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); + silence = 0xff; /* ulaw silence */ + } else + silence = 0x2a; /* alaw silence */ + if (!(type[HFC_cnt] & 0x200)) + test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); + + if (type[HFC_cnt] & 0x800) + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + if (type[HFC_cnt] & 0x1000) { + test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); + test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + } + if (type[HFC_cnt] & 0x4000) + test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip); + if (type[HFC_cnt] & 0x8000) + test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip); + hc->slots = 32; + if (type[HFC_cnt] & 0x10000) + hc->slots = 64; + if (type[HFC_cnt] & 0x20000) + hc->slots = 128; + if (type[HFC_cnt] & 0x80000) { + test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip); + hc->wdcount = 0; + hc->wdbyte = V_GPIO_OUT2; + printk(KERN_NOTICE "Watchdog enabled\n"); + } + + /* setup pci, hc->slots may change due to PLXSD */ + ret_err = setup_pci(hc, pdev, ent); + if (ret_err) { + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + return ret_err; + } + + /* crate channels */ + for (pt = 0; pt < hc->ports; pt++) { + if (Port_cnt >= MAX_PORTS) { + printk(KERN_ERR "too many ports (max=%d).\n", + MAX_PORTS); + ret_err = -EINVAL; + goto free_card; + } + if (hc->type == 1) + ret_err = init_e1_port(hc, m); + else + ret_err = init_multi_port(hc, pt); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: Registering D-channel, card(%d) port(%d)" + "result %d\n", + __func__, HFC_cnt + 1, pt, ret_err); + + if (ret_err) { + while (pt) { /* release already registered ports */ + pt--; + release_port(hc, hc->chan[(pt << 2) + 2].dch); + } + goto free_card; + } + Port_cnt++; + } + + /* disp switches */ + switch (m->dip_type) { + case DIP_4S: + /* + * get DIP Setting for beroNet 1S/2S/4S cards + * check if Port Jumper config matches + * module param 'protocol' + * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) + + * GPI 19/23 (R_GPI_IN2)) + */ + dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) | + ((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) | + (~HFC_inb(hc, R_GPI_IN2) & 0x08); + + /* Port mode (TE/NT) jumpers */ + pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4) & 0xf); + + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + pmj = ~pmj & 0xf; + + printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n", + m->vendor_name, m->card_name, dips, pmj); + break; + case DIP_8S: + /* + * get DIP Setting for beroNet 8S0+ cards + * + * enable PCI auxbridge function + */ + HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); + /* prepare access to auxport */ + outw(0x4000, hc->pci_iobase + 4); + /* + * some dummy reads are required to + * read valid DIP switch data + */ + dips = inb(hc->pci_iobase); + dips = inb(hc->pci_iobase); + dips = inb(hc->pci_iobase); + dips = ~inb(hc->pci_iobase) & 0x3F; + outw(0x0, hc->pci_iobase + 4); + /* disable PCI auxbridge function */ + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + printk(KERN_INFO "%s: %s DIPs(0x%x)\n", + m->vendor_name, m->card_name, dips); + break; + case DIP_E1: + /* + * get DIP Setting for beroNet E1 cards + * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0) + */ + dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0)>>4; + printk(KERN_INFO "%s: %s DIPs(0x%x)\n", + m->vendor_name, m->card_name, dips); + break; + } + + /* add to list */ + spin_lock_irqsave(&HFClock, flags); + list_add_tail(&hc->list, &HFClist); + spin_unlock_irqrestore(&HFClock, flags); + + /* initialize hardware */ + ret_err = init_card(hc); + if (ret_err) { + printk(KERN_ERR "init card returns %d\n", ret_err); + release_card(hc); + return ret_err; + } + + /* start IRQ and return */ + spin_lock_irqsave(&hc->lock, flags); + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + return 0; + +free_card: + release_io_hfcmulti(hc); + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + return ret_err; +} + +static void __devexit hfc_remove_pci(struct pci_dev *pdev) +{ + struct hfc_multi *card = pci_get_drvdata(pdev); + u_long flags; + + if (debug) + printk(KERN_INFO "removing hfc_multi card vendor:%x " + "device:%x subvendor:%x subdevice:%x\n", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); + + if (card) { + spin_lock_irqsave(&HFClock, flags); + release_card(card); + spin_unlock_irqrestore(&HFClock, flags); + } else { + if (debug) + printk(KERN_WARNING "%s: drvdata allready removed\n", + __func__); + } +} + +#define VENDOR_CCD "Cologne Chip AG" +#define VENDOR_BN "beroNet GmbH" +#define VENDOR_DIG "Digium Inc." +#define VENDOR_JH "Junghanns.NET GmbH" +#define VENDOR_PRIM "PrimuX" + +static const struct hm_map hfcm_map[] = { +/*0*/ {VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0}, +/*1*/ {VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S}, +/*2*/ {VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0}, +/*3*/ {VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0}, +/*4*/ {VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0}, +/*5*/ {VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0}, +/*6*/ {VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, 0, 0}, +/*7*/ {VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0}, +/*8*/ {VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO}, +/*9*/ {VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0}, +/*10*/ {VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0}, +/*11*/ {VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0}, + +/*12*/ {VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0}, +/*13*/ {VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S, + HFC_IO_MODE_REGIO}, +/*14*/ {VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0}, +/*15*/ {VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0}, + +/*16*/ {VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0}, +/*17*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0}, +/*18*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0}, + +/*19*/ {VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0}, +/*20*/ {VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0}, +/*21*/ {VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0}, +/*22*/ {VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0}, + +/*23*/ {VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0}, +/*24*/ {VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0}, +/*25*/ {VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0}, + +/*26*/ {VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0, + HFC_IO_MODE_PLXSD}, +/*27*/ {VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0, + HFC_IO_MODE_PLXSD}, +/*28*/ {VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0}, +/*29*/ {VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0}, +/*30*/ {VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0}, +}; + +#undef H +#define H(x) ((unsigned long)&hfcm_map[x]) +static struct pci_device_id hfmultipci_ids[] __devinitdata = { + + /* Cards with HFC-4S Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */ + { PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, + PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */ + + /* Cards with HFC-8S Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, + /* IOB8ST Recording */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */ + + + /* Cards with HFC-E1 Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */ + + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */ + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0}, + {0, } +}; +#undef H + +MODULE_DEVICE_TABLE(pci, hfmultipci_ids); + +static int +hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + int ret; + + if (m == NULL) { + if (ent->vendor == PCI_VENDOR_ID_CCD) + if (ent->device == PCI_DEVICE_ID_CCD_HFC4S || + ent->device == PCI_DEVICE_ID_CCD_HFC8S || + ent->device == PCI_DEVICE_ID_CCD_HFCE1) + printk(KERN_ERR + "unknown HFC multiport controller " + "(vendor:%x device:%x subvendor:%x " + "subdevice:%x) Please contact the " + "driver maintainer for support.\n", + ent->vendor, ent->device, + ent->subvendor, ent->subdevice); + return -ENODEV; + } + ret = hfcmulti_init(pdev, ent); + if (ret) + return ret; + HFC_cnt++; + printk(KERN_INFO "%d devices registered\n", HFC_cnt); + return 0; +} + +static struct pci_driver hfcmultipci_driver = { + .name = "hfc_multi", + .probe = hfcmulti_probe, + .remove = __devexit_p(hfc_remove_pci), + .id_table = hfmultipci_ids, +}; + +static void __exit +HFCmulti_cleanup(void) +{ + struct hfc_multi *card, *next; + + /* unload interrupt function symbol */ + if (hfc_interrupt) + symbol_put(ztdummy_extern_interrupt); + if (register_interrupt) + symbol_put(ztdummy_register_interrupt); + if (unregister_interrupt) { + if (interrupt_registered) { + interrupt_registered = 0; + unregister_interrupt(); + } + symbol_put(ztdummy_unregister_interrupt); + } + + list_for_each_entry_safe(card, next, &HFClist, list) + release_card(card); + /* get rid of all devices of this driver */ + pci_unregister_driver(&hfcmultipci_driver); +} + +static int __init +HFCmulti_init(void) +{ + int err; + +#ifdef IRQ_DEBUG + printk(KERN_ERR "%s: IRQ_DEBUG IS ENABLED!\n", __func__); +#endif + + spin_lock_init(&HFClock); + spin_lock_init(&plx_lock); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: init entered\n", __func__); + +#ifdef __BIG_ENDIAN +#error "not running on big endian machines now" +#endif + hfc_interrupt = symbol_get(ztdummy_extern_interrupt); + register_interrupt = symbol_get(ztdummy_register_interrupt); + unregister_interrupt = symbol_get(ztdummy_unregister_interrupt); + printk(KERN_INFO "mISDN: HFC-multi driver %s\n", + hfcmulti_revision); + + switch (poll) { + case 0: + poll_timer = 6; + poll = 128; + break; + /* + * wenn dieses break nochmal verschwindet, + * gibt es heisse ohren :-) + * "without the break you will get hot ears ???" + */ + case 8: + poll_timer = 2; + break; + case 16: + poll_timer = 3; + break; + case 32: + poll_timer = 4; + break; + case 64: + poll_timer = 5; + break; + case 128: + poll_timer = 6; + break; + case 256: + poll_timer = 7; + break; + default: + printk(KERN_ERR + "%s: Wrong poll value (%d).\n", __func__, poll); + err = -EINVAL; + return err; + + } + + err = pci_register_driver(&hfcmultipci_driver); + if (err < 0) { + printk(KERN_ERR "error registering pci driver: %x\n", err); + if (hfc_interrupt) + symbol_put(ztdummy_extern_interrupt); + if (register_interrupt) + symbol_put(ztdummy_register_interrupt); + if (unregister_interrupt) { + if (interrupt_registered) { + interrupt_registered = 0; + unregister_interrupt(); + } + symbol_put(ztdummy_unregister_interrupt); + } + return err; + } + return 0; +} + + +module_init(HFCmulti_init); +module_exit(HFCmulti_cleanup); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index c3b1761aba26..ffe479ba0779 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1832,7 +1832,13 @@ #define PCI_DEVICE_ID_MOXA_C320 0x3200 #define PCI_VENDOR_ID_CCD 0x1397 +#define PCI_DEVICE_ID_CCD_HFC4S 0x08B4 +#define PCI_SUBDEVICE_ID_CCD_PMX2S 0x1234 +#define PCI_DEVICE_ID_CCD_HFC8S 0x16B8 #define PCI_DEVICE_ID_CCD_2BD0 0x2bd0 +#define PCI_DEVICE_ID_CCD_HFCE1 0x30B1 +#define PCI_SUBDEVICE_ID_CCD_SPD4S 0x3136 +#define PCI_SUBDEVICE_ID_CCD_SPDE1 0x3137 #define PCI_DEVICE_ID_CCD_B000 0xb000 #define PCI_DEVICE_ID_CCD_B006 0xb006 #define PCI_DEVICE_ID_CCD_B007 0xb007 @@ -1842,8 +1848,32 @@ #define PCI_DEVICE_ID_CCD_B00B 0xb00b #define PCI_DEVICE_ID_CCD_B00C 0xb00c #define PCI_DEVICE_ID_CCD_B100 0xb100 +#define PCI_SUBDEVICE_ID_CCD_IOB4ST 0xB520 +#define PCI_SUBDEVICE_ID_CCD_IOB8STR 0xB521 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST 0xB522 +#define PCI_SUBDEVICE_ID_CCD_IOB1E1 0xB523 +#define PCI_SUBDEVICE_ID_CCD_SWYX4S 0xB540 +#define PCI_SUBDEVICE_ID_CCD_JH4S20 0xB550 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1 0xB552 +#define PCI_SUBDEVICE_ID_CCD_BN4S 0xB560 +#define PCI_SUBDEVICE_ID_CCD_BN8S 0xB562 +#define PCI_SUBDEVICE_ID_CCD_BNE1 0xB563 +#define PCI_SUBDEVICE_ID_CCD_BNE1D 0xB564 +#define PCI_SUBDEVICE_ID_CCD_BNE1DP 0xB565 +#define PCI_SUBDEVICE_ID_CCD_BN2S 0xB566 +#define PCI_SUBDEVICE_ID_CCD_BN1SM 0xB567 +#define PCI_SUBDEVICE_ID_CCD_BN4SM 0xB568 +#define PCI_SUBDEVICE_ID_CCD_BN2SM 0xB569 +#define PCI_SUBDEVICE_ID_CCD_BNE1M 0xB56A +#define PCI_SUBDEVICE_ID_CCD_BN8SP 0xB56B +#define PCI_SUBDEVICE_ID_CCD_HFC4S 0xB620 +#define PCI_SUBDEVICE_ID_CCD_HFC8S 0xB622 #define PCI_DEVICE_ID_CCD_B700 0xb700 #define PCI_DEVICE_ID_CCD_B701 0xb701 +#define PCI_SUBDEVICE_ID_CCD_HFCE1 0xC523 +#define PCI_SUBDEVICE_ID_CCD_OV2S 0xE884 +#define PCI_SUBDEVICE_ID_CCD_OV4S 0xE888 +#define PCI_SUBDEVICE_ID_CCD_OV8S 0xE998 #define PCI_VENDOR_ID_EXAR 0x13a8 #define PCI_DEVICE_ID_EXAR_XR17C152 0x0152 @@ -2523,6 +2553,9 @@ #define PCI_VENDOR_ID_3COM_2 0xa727 +#define PCI_VENDOR_ID_DIGIUM 0xd161 +#define PCI_DEVICE_ID_DIGIUM_HFC4S 0xb410 + #define PCI_SUBVENDOR_ID_EXSYS 0xd84d #define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014 #define PCI_SUBDEVICE_ID_EXSYS_4055 0x4055 -- cgit From 93bc4e89c260d91576840c4881d1066d84ccd422 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Sat, 26 Jul 2008 17:49:33 -0700 Subject: netfilter: fix double-free and use-after free As suggested by Patrick McHardy, introduce a __krealloc() that doesn't free the original buffer to fix a double-free and use-after-free bug introduced by me in netfilter that uses RCU. Reported-by: Patrick McHardy Signed-off-by: Pekka Enberg Tested-by: Dieter Ries Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/slab.h | 1 + mm/util.c | 44 ++++++++++++++++++++++++++++--------- net/netfilter/nf_conntrack_extend.c | 2 +- 3 files changed, 36 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/slab.h b/include/linux/slab.h index 9aa90a6f20e0..be6f1d40b66a 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -96,6 +96,7 @@ int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr); /* * Common kmalloc functions provided by all allocators */ +void * __must_check __krealloc(const void *, size_t, gfp_t); void * __must_check krealloc(const void *, size_t, gfp_t); void kfree(const void *); size_t ksize(const void *); diff --git a/mm/util.c b/mm/util.c index 8f18683825bc..6ef9e9943f62 100644 --- a/mm/util.c +++ b/mm/util.c @@ -68,25 +68,22 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp) EXPORT_SYMBOL(kmemdup); /** - * krealloc - reallocate memory. The contents will remain unchanged. + * __krealloc - like krealloc() but don't free @p. * @p: object to reallocate memory for. * @new_size: how many bytes of memory are required. * @flags: the type of memory to allocate. * - * The contents of the object pointed to are preserved up to the - * lesser of the new and old sizes. If @p is %NULL, krealloc() - * behaves exactly like kmalloc(). If @size is 0 and @p is not a - * %NULL pointer, the object pointed to is freed. + * This function is like krealloc() except it never frees the originally + * allocated buffer. Use this if you don't want to free the buffer immediately + * like, for example, with RCU. */ -void *krealloc(const void *p, size_t new_size, gfp_t flags) +void *__krealloc(const void *p, size_t new_size, gfp_t flags) { void *ret; size_t ks = 0; - if (unlikely(!new_size)) { - kfree(p); + if (unlikely(!new_size)) return ZERO_SIZE_PTR; - } if (p) ks = ksize(p); @@ -95,10 +92,37 @@ void *krealloc(const void *p, size_t new_size, gfp_t flags) return (void *)p; ret = kmalloc_track_caller(new_size, flags); - if (ret && p) { + if (ret && p) memcpy(ret, p, ks); + + return ret; +} +EXPORT_SYMBOL(__krealloc); + +/** + * krealloc - reallocate memory. The contents will remain unchanged. + * @p: object to reallocate memory for. + * @new_size: how many bytes of memory are required. + * @flags: the type of memory to allocate. + * + * The contents of the object pointed to are preserved up to the + * lesser of the new and old sizes. If @p is %NULL, krealloc() + * behaves exactly like kmalloc(). If @size is 0 and @p is not a + * %NULL pointer, the object pointed to is freed. + */ +void *krealloc(const void *p, size_t new_size, gfp_t flags) +{ + void *ret; + + if (unlikely(!new_size)) { kfree(p); + return ZERO_SIZE_PTR; } + + ret = __krealloc(p, new_size, flags); + if (ret && p != ret) + kfree(p); + return ret; } EXPORT_SYMBOL(krealloc); diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 3469bc71a385..c956ef7eeecb 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -95,7 +95,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) newlen = newoff + t->len; rcu_read_unlock(); - new = krealloc(ct->ext, newlen, gfp); + new = __krealloc(ct->ext, newlen, gfp); if (!new) return NULL; -- cgit From d2d9648ec6858e19d16a0b16da62534e85888653 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 1 Jul 2008 14:16:09 +0200 Subject: [PATCH] reuse xxx_fifo_fops for xxx_pipe_fops Merge fifo and pipe file_operations. Signed-off-by: Denys Vlasenko Signed-off-by: Al Viro --- fs/fifo.c | 8 ++++---- fs/pipe.c | 51 ++++++++------------------------------------------- include/linux/fs.h | 6 +++--- 3 files changed, 15 insertions(+), 50 deletions(-) (limited to 'include/linux') diff --git a/fs/fifo.c b/fs/fifo.c index 9785e36f81e7..987bf9411495 100644 --- a/fs/fifo.c +++ b/fs/fifo.c @@ -57,7 +57,7 @@ static int fifo_open(struct inode *inode, struct file *filp) * POSIX.1 says that O_NONBLOCK means return with the FIFO * opened, even when there is no process writing the FIFO. */ - filp->f_op = &read_fifo_fops; + filp->f_op = &read_pipefifo_fops; pipe->r_counter++; if (pipe->readers++ == 0) wake_up_partner(inode); @@ -86,7 +86,7 @@ static int fifo_open(struct inode *inode, struct file *filp) if ((filp->f_flags & O_NONBLOCK) && !pipe->readers) goto err; - filp->f_op = &write_fifo_fops; + filp->f_op = &write_pipefifo_fops; pipe->w_counter++; if (!pipe->writers++) wake_up_partner(inode); @@ -105,7 +105,7 @@ static int fifo_open(struct inode *inode, struct file *filp) * This implementation will NEVER block on a O_RDWR open, since * the process can at least talk to itself. */ - filp->f_op = &rdwr_fifo_fops; + filp->f_op = &rdwr_pipefifo_fops; pipe->readers++; pipe->writers++; @@ -151,5 +151,5 @@ err_nocleanup: * depending on the access mode of the file... */ const struct file_operations def_fifo_fops = { - .open = fifo_open, /* will set read or write pipe_fops */ + .open = fifo_open, /* will set read_ or write_pipefifo_fops */ }; diff --git a/fs/pipe.c b/fs/pipe.c index 10c4e9aa5c49..fcba6542b8d0 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -777,45 +777,10 @@ pipe_rdwr_open(struct inode *inode, struct file *filp) /* * The file_operations structs are not static because they * are also used in linux/fs/fifo.c to do operations on FIFOs. + * + * Pipes reuse fifos' file_operations structs. */ -const struct file_operations read_fifo_fops = { - .llseek = no_llseek, - .read = do_sync_read, - .aio_read = pipe_read, - .write = bad_pipe_w, - .poll = pipe_poll, - .unlocked_ioctl = pipe_ioctl, - .open = pipe_read_open, - .release = pipe_read_release, - .fasync = pipe_read_fasync, -}; - -const struct file_operations write_fifo_fops = { - .llseek = no_llseek, - .read = bad_pipe_r, - .write = do_sync_write, - .aio_write = pipe_write, - .poll = pipe_poll, - .unlocked_ioctl = pipe_ioctl, - .open = pipe_write_open, - .release = pipe_write_release, - .fasync = pipe_write_fasync, -}; - -const struct file_operations rdwr_fifo_fops = { - .llseek = no_llseek, - .read = do_sync_read, - .aio_read = pipe_read, - .write = do_sync_write, - .aio_write = pipe_write, - .poll = pipe_poll, - .unlocked_ioctl = pipe_ioctl, - .open = pipe_rdwr_open, - .release = pipe_rdwr_release, - .fasync = pipe_rdwr_fasync, -}; - -static const struct file_operations read_pipe_fops = { +const struct file_operations read_pipefifo_fops = { .llseek = no_llseek, .read = do_sync_read, .aio_read = pipe_read, @@ -827,7 +792,7 @@ static const struct file_operations read_pipe_fops = { .fasync = pipe_read_fasync, }; -static const struct file_operations write_pipe_fops = { +const struct file_operations write_pipefifo_fops = { .llseek = no_llseek, .read = bad_pipe_r, .write = do_sync_write, @@ -839,7 +804,7 @@ static const struct file_operations write_pipe_fops = { .fasync = pipe_write_fasync, }; -static const struct file_operations rdwr_pipe_fops = { +const struct file_operations rdwr_pipefifo_fops = { .llseek = no_llseek, .read = do_sync_read, .aio_read = pipe_read, @@ -927,7 +892,7 @@ static struct inode * get_pipe_inode(void) inode->i_pipe = pipe; pipe->readers = pipe->writers = 1; - inode->i_fop = &rdwr_pipe_fops; + inode->i_fop = &rdwr_pipefifo_fops; /* * Mark the inode dirty from the very beginning, @@ -978,7 +943,7 @@ struct file *create_write_pipe(int flags) d_instantiate(dentry, inode); err = -ENFILE; - f = alloc_file(pipe_mnt, dentry, FMODE_WRITE, &write_pipe_fops); + f = alloc_file(pipe_mnt, dentry, FMODE_WRITE, &write_pipefifo_fops); if (!f) goto err_dentry; f->f_mapping = inode->i_mapping; @@ -1020,7 +985,7 @@ struct file *create_read_pipe(struct file *wrf, int flags) f->f_pos = 0; f->f_flags = O_RDONLY | (flags & O_NONBLOCK); - f->f_op = &read_pipe_fops; + f->f_op = &read_pipefifo_fops; f->f_mode = FMODE_READ; f->f_version = 0; diff --git a/include/linux/fs.h b/include/linux/fs.h index 53d2edb709b3..7721a2ac9c0e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1696,9 +1696,9 @@ extern void init_special_inode(struct inode *, umode_t, dev_t); extern void make_bad_inode(struct inode *); extern int is_bad_inode(struct inode *); -extern const struct file_operations read_fifo_fops; -extern const struct file_operations write_fifo_fops; -extern const struct file_operations rdwr_fifo_fops; +extern const struct file_operations read_pipefifo_fops; +extern const struct file_operations write_pipefifo_fops; +extern const struct file_operations rdwr_pipefifo_fops; extern int fs_may_remount_ro(struct super_block *); -- cgit From 734550921e9b7ab924a43aa3d0bd4239dac4fbf1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 14 Jul 2008 21:22:20 -0400 Subject: [PATCH] beginning of sysctl cleanup - ctl_table_set New object: set of sysctls [currently - root and per-net-ns]. Contains: pointer to parent set, list of tables and "should I see this set?" method (->is_seen(set)). Current lists of tables are subsumed by that; net-ns contains such a beast. ->lookup() for ctl_table_root returns pointer to ctl_table_set instead of that to ->list of that ctl_table_set. [folded compile fixes by rdd for configs without sysctl] Signed-off-by: Al Viro --- include/linux/sysctl.h | 15 +++++++++++++-- include/net/net_namespace.h | 4 +++- kernel/sysctl.c | 41 +++++++++++++++++++++++++++++++---------- net/sysctl_net.c | 22 ++++++++++------------ 4 files changed, 57 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 24141b4d1a11..c1e0cf408af9 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -947,6 +947,16 @@ struct ctl_table; struct nsproxy; struct ctl_table_root; +struct ctl_table_set { + struct list_head list; + struct ctl_table_set *parent; + int (*is_seen)(struct ctl_table_set *); +}; + +extern void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_set *parent, + int (*is_seen)(struct ctl_table_set *)); + extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, struct ctl_table_header *prev); @@ -1049,8 +1059,8 @@ struct ctl_table struct ctl_table_root { struct list_head root_list; - struct list_head header_list; - struct list_head *(*lookup)(struct ctl_table_root *root, + struct ctl_table_set default_set; + struct ctl_table_set *(*lookup)(struct ctl_table_root *root, struct nsproxy *namespaces); int (*permissions)(struct ctl_table_root *root, struct nsproxy *namespaces, struct ctl_table *table); @@ -1066,6 +1076,7 @@ struct ctl_table_header struct completion *unregistering; struct ctl_table *ctl_table_arg; struct ctl_table_root *root; + struct ctl_table_set *set; }; /* struct ctl_path describes where in the hierarchy a table is added */ diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 3855620b78a9..a8eb43cf0c7e 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -38,7 +38,9 @@ struct net { struct proc_dir_entry *proc_net; struct proc_dir_entry *proc_net_stat; - struct list_head sysctl_table_headers; +#ifdef CONFIG_SYSCTL + struct ctl_table_set sysctls; +#endif struct net_device *loopback_dev; /* The loopback */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 35a50db9b6ce..8ee4a0619fbb 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -160,12 +160,13 @@ static struct ctl_table root_table[]; static struct ctl_table_root sysctl_table_root; static struct ctl_table_header root_table_header = { .ctl_table = root_table, - .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.header_list), + .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list), .root = &sysctl_table_root, + .set = &sysctl_table_root.default_set, }; static struct ctl_table_root sysctl_table_root = { .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), - .header_list = LIST_HEAD_INIT(root_table_header.ctl_entry), + .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry), }; static struct ctl_table kern_table[]; @@ -1403,14 +1404,20 @@ void sysctl_head_finish(struct ctl_table_header *head) spin_unlock(&sysctl_lock); } +static struct ctl_table_set * +lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces) +{ + struct ctl_table_set *set = &root->default_set; + if (root->lookup) + set = root->lookup(root, namespaces); + return set; +} + static struct list_head * lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) { - struct list_head *header_list; - header_list = &root->header_list; - if (root->lookup) - header_list = root->lookup(root, namespaces); - return header_list; + struct ctl_table_set *set = lookup_header_set(root, namespaces); + return &set->list; } struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, @@ -1720,7 +1727,6 @@ struct ctl_table_header *__register_sysctl_paths( struct nsproxy *namespaces, const struct ctl_path *path, struct ctl_table *table) { - struct list_head *header_list; struct ctl_table_header *header; struct ctl_table *new, **prevp; unsigned int n, npath; @@ -1772,8 +1778,8 @@ struct ctl_table_header *__register_sysctl_paths( } #endif spin_lock(&sysctl_lock); - header_list = lookup_header_list(root, namespaces); - list_add_tail(&header->ctl_entry, header_list); + header->set = lookup_header_set(root, namespaces); + list_add_tail(&header->ctl_entry, &header->set->list); spin_unlock(&sysctl_lock); return header; @@ -1832,6 +1838,15 @@ void unregister_sysctl_table(struct ctl_table_header * header) kfree(header); } +void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_set *parent, + int (*is_seen)(struct ctl_table_set *)) +{ + INIT_LIST_HEAD(&p->list); + p->parent = parent ? parent : &sysctl_table_root.default_set; + p->is_seen = is_seen; +} + #else /* !CONFIG_SYSCTL */ struct ctl_table_header *register_sysctl_table(struct ctl_table * table) { @@ -1848,6 +1863,12 @@ void unregister_sysctl_table(struct ctl_table_header * table) { } +void setup_sysctl_set(struct ctl_table_set *p, + struct ctl_table_set *parent, + int (*is_seen)(struct ctl_table_set *)) +{ +} + #endif /* CONFIG_SYSCTL */ /* diff --git a/net/sysctl_net.c b/net/sysctl_net.c index 63ada437fc2f..cefbc367d8be 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -29,10 +29,15 @@ #include #endif -static struct list_head * +static struct ctl_table_set * net_ctl_header_lookup(struct ctl_table_root *root, struct nsproxy *namespaces) { - return &namespaces->net_ns->sysctl_table_headers; + return &namespaces->net_ns->sysctls; +} + +static int is_seen(struct ctl_table_set *set) +{ + return ¤t->nsproxy->net_ns->sysctls == set; } /* Return standard mode bits for table entry. */ @@ -53,13 +58,6 @@ static struct ctl_table_root net_sysctl_root = { .permissions = net_ctl_permissions, }; -static LIST_HEAD(net_sysctl_ro_tables); -static struct list_head *net_ctl_ro_header_lookup(struct ctl_table_root *root, - struct nsproxy *namespaces) -{ - return &net_sysctl_ro_tables; -} - static int net_ctl_ro_header_perms(struct ctl_table_root *root, struct nsproxy *namespaces, struct ctl_table *table) { @@ -70,19 +68,18 @@ static int net_ctl_ro_header_perms(struct ctl_table_root *root, } static struct ctl_table_root net_sysctl_ro_root = { - .lookup = net_ctl_ro_header_lookup, .permissions = net_ctl_ro_header_perms, }; static int sysctl_net_init(struct net *net) { - INIT_LIST_HEAD(&net->sysctl_table_headers); + setup_sysctl_set(&net->sysctls, NULL, is_seen); return 0; } static void sysctl_net_exit(struct net *net) { - WARN_ON(!list_empty(&net->sysctl_table_headers)); + WARN_ON(!list_empty(&net->sysctls.list)); return; } @@ -98,6 +95,7 @@ static __init int sysctl_init(void) if (ret) goto out; register_sysctl_root(&net_sysctl_root); + setup_sysctl_set(&net_sysctl_ro_root.default_set, NULL, NULL); register_sysctl_root(&net_sysctl_ro_root); out: return ret; -- cgit From f7e6ced4061da509f737541ca4dbd44d83a6e82f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 15 Jul 2008 01:44:23 -0400 Subject: [PATCH] allow delayed freeing of ctl_table_header Refcount the sucker; instead of freeing it by the end of unregistration just drop the refcount and free only when it hits zero. Make sure that we _always_ make ->unregistering non-NULL in start_unregistering(). That allows anybody to get a reference to such puppy, preventing its freeing and reuse. It does *not* block unregistration. Anybody who holds such a reference can * try to grab a "use" reference (ctl_head_grab()); that will succeeds if and only if it hadn't entered unregistration yet. If it succeeds, we can use it in all normal ways until we release the "use" reference (with ctl_head_finish()). Note that this relies on having ->unregistering become non-NULL in all cases when one starts to unregister the sucker. * keep pointers to ctl_table entries; they *can* be freed if the entire thing is unregistered. However, if ctl_head_grab() succeeds, we know that unregistration had not happened (and will not happen until ctl_head_finish()) and such pointers can be used safely. IOW, now we can have inodes under /proc/sys keep references to ctl_table entries, protecting them with references to ctl_table_header and grabbing the latter for the duration of operations that require access to ctl_table. That won't cause deadlocks, since unregistration will not be stopped by mere keeping a reference to ctl_table_header. Signed-off-by: Al Viro --- include/linux/sysctl.h | 6 ++++++ kernel/sysctl.c | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index c1e0cf408af9..956264d09ba0 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -957,6 +957,11 @@ extern void setup_sysctl_set(struct ctl_table_set *p, struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)); +struct ctl_table_header; + +extern void sysctl_head_get(struct ctl_table_header *); +extern void sysctl_head_put(struct ctl_table_header *); +extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *); extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, struct ctl_table_header *prev); @@ -1073,6 +1078,7 @@ struct ctl_table_header struct ctl_table *ctl_table; struct list_head ctl_entry; int used; + int count; struct completion *unregistering; struct ctl_table *ctl_table_arg; struct ctl_table_root *root; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8ee4a0619fbb..60d9357e7172 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1387,6 +1387,9 @@ static void start_unregistering(struct ctl_table_header *p) spin_unlock(&sysctl_lock); wait_for_completion(&wait); spin_lock(&sysctl_lock); + } else { + /* anything non-NULL; we'll never dereference it */ + p->unregistering = ERR_PTR(-EINVAL); } /* * do not remove from the list until nobody holds it; walking the @@ -1395,6 +1398,32 @@ static void start_unregistering(struct ctl_table_header *p) list_del_init(&p->ctl_entry); } +void sysctl_head_get(struct ctl_table_header *head) +{ + spin_lock(&sysctl_lock); + head->count++; + spin_unlock(&sysctl_lock); +} + +void sysctl_head_put(struct ctl_table_header *head) +{ + spin_lock(&sysctl_lock); + if (!--head->count) + kfree(head); + spin_unlock(&sysctl_lock); +} + +struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) +{ + if (!head) + BUG(); + spin_lock(&sysctl_lock); + if (!use_table(head)) + head = ERR_PTR(-ENOENT); + spin_unlock(&sysctl_lock); + return head; +} + void sysctl_head_finish(struct ctl_table_header *head) { if (!head) @@ -1771,6 +1800,7 @@ struct ctl_table_header *__register_sysctl_paths( header->unregistering = NULL; header->root = root; sysctl_set_parent(NULL, header->ctl_table); + header->count = 1; #ifdef CONFIG_SYSCTL_SYSCALL_CHECK if (sysctl_check_table(namespaces, header->ctl_table)) { kfree(header); @@ -1834,8 +1864,9 @@ void unregister_sysctl_table(struct ctl_table_header * header) spin_lock(&sysctl_lock); start_unregistering(header); + if (!--header->count) + kfree(header); spin_unlock(&sysctl_lock); - kfree(header); } void setup_sysctl_set(struct ctl_table_set *p, @@ -1869,6 +1900,10 @@ void setup_sysctl_set(struct ctl_table_set *p, { } +void sysctl_head_put(struct ctl_table_header *head) +{ +} + #endif /* CONFIG_SYSCTL */ /* -- cgit From ae7edecc9b8810770a8e5cb9a466ea4bdcfa8401 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 15 Jul 2008 06:33:31 -0400 Subject: [PATCH] sysctl: keep track of tree relationships In a sense, that's the heart of the series. It's based on the following property of the trees we are actually asked to add: they can be split into stem that is already covered by registered trees and crown that is entirely new. IOW, if a/b and a/c/d are introduced by our tree, then a/c is also introduced by it. That allows to associate tree and table entry with each node in the union; while directory nodes might be covered by many trees, only one will cover the node by its crown. And that will allow much saner logics for /proc/sys in the next patches. This patch introduces the data structures needed to keep track of that. When adding a sysctl table, we find a "parent" one. Which is to say, find the deepest node on its stem that already is present in one of the tables from our table set or its ancestor sets. That table will be our parent and that node in it - attachment point. Add our table to list anchored in parent, have it refer the parent and contents of attachment point. Also remember where its crown lives. Signed-off-by: Al Viro --- include/linux/sysctl.h | 3 +++ kernel/sysctl.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 956264d09ba0..3f6599aeb0db 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -1083,6 +1083,9 @@ struct ctl_table_header struct ctl_table *ctl_table_arg; struct ctl_table_root *root; struct ctl_table_set *set; + struct ctl_table *attached_by; + struct ctl_table *attached_to; + struct ctl_table_header *parent; }; /* struct ctl_path describes where in the hierarchy a table is added */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 60d9357e7172..c9a0af887033 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1680,6 +1680,52 @@ static __init int sysctl_init(void) core_initcall(sysctl_init); +static int is_branch_in(struct ctl_table *branch, struct ctl_table *table) +{ + struct ctl_table *p; + const char *s = branch->procname; + + /* branch should have named subdirectory as its first element */ + if (!s || !branch->child) + return 0; + + /* ... and nothing else */ + if (branch[1].procname || branch[1].ctl_name) + return 0; + + /* table should contain subdirectory with the same name */ + for (p = table; p->procname || p->ctl_name; p++) { + if (!p->child) + continue; + if (p->procname && strcmp(p->procname, s) == 0) + return 1; + } + return 0; +} + +/* see if attaching q to p would be an improvement */ +static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q) +{ + struct ctl_table *to = p->ctl_table, *by = q->ctl_table; + int is_better = 0; + int not_in_parent = !p->attached_by; + + while (is_branch_in(by, to)) { + if (by == q->attached_by) + is_better = 1; + if (to == p->attached_by) + not_in_parent = 1; + by = by->child; + to = to->child; + } + + if (is_better && not_in_parent) { + q->attached_by = by; + q->attached_to = to; + q->parent = p; + } +} + /** * __register_sysctl_paths - register a sysctl hierarchy * @root: List of sysctl headers to register on @@ -1759,6 +1805,7 @@ struct ctl_table_header *__register_sysctl_paths( struct ctl_table_header *header; struct ctl_table *new, **prevp; unsigned int n, npath; + struct ctl_table_set *set; /* Count the path components */ for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) @@ -1809,6 +1856,18 @@ struct ctl_table_header *__register_sysctl_paths( #endif spin_lock(&sysctl_lock); header->set = lookup_header_set(root, namespaces); + header->attached_by = header->ctl_table; + header->attached_to = root_table; + header->parent = &root_table_header; + for (set = header->set; set; set = set->parent) { + struct ctl_table_header *p; + list_for_each_entry(p, &set->list, ctl_entry) { + if (p->unregistering) + continue; + try_attach(p, header); + } + } + header->parent->count++; list_add_tail(&header->ctl_entry, &header->set->list); spin_unlock(&sysctl_lock); @@ -1864,6 +1923,10 @@ void unregister_sysctl_table(struct ctl_table_header * header) spin_lock(&sysctl_lock); start_unregistering(header); + if (!--header->parent->count) { + WARN_ON(1); + kfree(header->parent); + } if (!--header->count) kfree(header); spin_unlock(&sysctl_lock); -- cgit From 9043476f726802f4b00c96d0c4f418dde48d1304 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 15 Jul 2008 08:54:06 -0400 Subject: [PATCH] sanitize proc_sysctl * keep references to ctl_table_head and ctl_table in /proc/sys inodes * grab the former during operations, use the latter for access to entry if that succeeds * have ->d_compare() check if table should be seen for one who does lookup; that allows us to avoid flipping inodes - if we have the same name resolve to different things, we'll just keep several dentries and ->d_compare() will reject the wrong ones. * have ->lookup() and ->readdir() scan the table of our inode first, then walk all ctl_table_header and scan ->attached_by for those that are attached to our directory. * implement ->getattr(). * get rid of insane amounts of tree-walking * get rid of the need to know dentry in ->permission() and of the contortions induced by that. Signed-off-by: Al Viro --- fs/proc/inode.c | 5 + fs/proc/proc_sysctl.c | 427 ++++++++++++++++++++++-------------------------- include/linux/proc_fs.h | 5 + include/linux/sysctl.h | 1 + kernel/sysctl.c | 15 ++ 5 files changed, 218 insertions(+), 235 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/inode.c b/fs/proc/inode.c index b37f25dc45a5..8bb03f056c28 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,8 @@ static void proc_delete_inode(struct inode *inode) module_put(de->owner); de_put(de); } + if (PROC_I(inode)->sysctl) + sysctl_head_put(PROC_I(inode)->sysctl); clear_inode(inode); } @@ -84,6 +87,8 @@ static struct inode *proc_alloc_inode(struct super_block *sb) ei->fd = 0; ei->op.proc_get_link = NULL; ei->pde = NULL; + ei->sysctl = NULL; + ei->sysctl_entry = NULL; inode = &ei->vfs_inode; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; return inode; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 5acc001d49f6..fa1ec2433e44 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -10,149 +10,110 @@ static struct dentry_operations proc_sys_dentry_operations; static const struct file_operations proc_sys_file_operations; static const struct inode_operations proc_sys_inode_operations; +static const struct file_operations proc_sys_dir_file_operations; +static const struct inode_operations proc_sys_dir_operations; -static void proc_sys_refresh_inode(struct inode *inode, struct ctl_table *table) -{ - /* Refresh the cached information bits in the inode */ - if (table) { - inode->i_uid = 0; - inode->i_gid = 0; - inode->i_mode = table->mode; - if (table->proc_handler) { - inode->i_mode |= S_IFREG; - inode->i_nlink = 1; - } else { - inode->i_mode |= S_IFDIR; - inode->i_nlink = 0; /* It is too hard to figure out */ - } - } -} - -static struct inode *proc_sys_make_inode(struct inode *dir, struct ctl_table *table) +static struct inode *proc_sys_make_inode(struct super_block *sb, + struct ctl_table_header *head, struct ctl_table *table) { struct inode *inode; - struct proc_inode *dir_ei, *ei; - int depth; + struct proc_inode *ei; - inode = new_inode(dir->i_sb); + inode = new_inode(sb); if (!inode) goto out; - /* A directory is always one deeper than it's parent */ - dir_ei = PROC_I(dir); - depth = dir_ei->fd + 1; - + sysctl_head_get(head); ei = PROC_I(inode); - ei->fd = depth; + ei->sysctl = head; + ei->sysctl_entry = table; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->i_op = &proc_sys_inode_operations; - inode->i_fop = &proc_sys_file_operations; inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */ - proc_sys_refresh_inode(inode, table); + inode->i_mode = table->mode; + if (!table->child) { + inode->i_mode |= S_IFREG; + inode->i_op = &proc_sys_inode_operations; + inode->i_fop = &proc_sys_file_operations; + } else { + inode->i_mode |= S_IFDIR; + inode->i_nlink = 0; + inode->i_op = &proc_sys_dir_operations; + inode->i_fop = &proc_sys_dir_file_operations; + } out: return inode; } -static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth) -{ - for (;;) { - struct proc_inode *ei; - - ei = PROC_I(dentry->d_inode); - if (ei->fd == depth) - break; /* found */ - - dentry = dentry->d_parent; - } - return dentry; -} - -static struct ctl_table *proc_sys_lookup_table_one(struct ctl_table *table, - struct qstr *name) +static struct ctl_table *find_in_table(struct ctl_table *p, struct qstr *name) { int len; - for ( ; table->ctl_name || table->procname; table++) { + for ( ; p->ctl_name || p->procname; p++) { - if (!table->procname) + if (!p->procname) continue; - len = strlen(table->procname); + len = strlen(p->procname); if (len != name->len) continue; - if (memcmp(table->procname, name->name, len) != 0) + if (memcmp(p->procname, name->name, len) != 0) continue; /* I have a match */ - return table; + return p; } return NULL; } -static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry, - struct ctl_table *table) +struct ctl_table_header *grab_header(struct inode *inode) { - struct dentry *ancestor; - struct proc_inode *ei; - int depth, i; + if (PROC_I(inode)->sysctl) + return sysctl_head_grab(PROC_I(inode)->sysctl); + else + return sysctl_head_next(NULL); +} - ei = PROC_I(dentry->d_inode); - depth = ei->fd; +static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct ctl_table_header *head = grab_header(dir); + struct ctl_table *table = PROC_I(dir)->sysctl_entry; + struct ctl_table_header *h = NULL; + struct qstr *name = &dentry->d_name; + struct ctl_table *p; + struct inode *inode; + struct dentry *err = ERR_PTR(-ENOENT); - if (depth == 0) - return table; + if (IS_ERR(head)) + return ERR_CAST(head); - for (i = 1; table && (i <= depth); i++) { - ancestor = proc_sys_ancestor(dentry, i); - table = proc_sys_lookup_table_one(table, &ancestor->d_name); - if (table) - table = table->child; + if (table && !table->child) { + WARN_ON(1); + goto out; } - return table; - -} -static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent, - struct qstr *name, - struct ctl_table *table) -{ - table = proc_sys_lookup_table(dparent, table); - if (table) - table = proc_sys_lookup_table_one(table, name); - return table; -} -static struct ctl_table *do_proc_sys_lookup(struct dentry *parent, - struct qstr *name, - struct ctl_table_header **ptr) -{ - struct ctl_table_header *head; - struct ctl_table *table = NULL; + table = table ? table->child : head->ctl_table; - for (head = sysctl_head_next(NULL); head; - head = sysctl_head_next(head)) { - table = proc_sys_lookup_entry(parent, name, head->ctl_table); - if (table) - break; + p = find_in_table(table, name); + if (!p) { + for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) { + if (h->attached_to != table) + continue; + p = find_in_table(h->attached_by, name); + if (p) + break; + } } - *ptr = head; - return table; -} - -static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) -{ - struct ctl_table_header *head; - struct inode *inode; - struct dentry *err; - struct ctl_table *table; - err = ERR_PTR(-ENOENT); - table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); - if (!table) + if (!p) goto out; err = ERR_PTR(-ENOMEM); - inode = proc_sys_make_inode(dir, table); + inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p); + if (h) + sysctl_head_finish(h); + if (!inode) goto out; @@ -168,22 +129,14 @@ out: static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf, size_t count, loff_t *ppos, int write) { - struct dentry *dentry = filp->f_dentry; - struct ctl_table_header *head; - struct ctl_table *table; + struct inode *inode = filp->f_path.dentry->d_inode; + struct ctl_table_header *head = grab_header(inode); + struct ctl_table *table = PROC_I(inode)->sysctl_entry; ssize_t error; size_t res; - table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); - /* Has the sysctl entry disappeared on us? */ - error = -ENOENT; - if (!table) - goto out; - - /* Has the sysctl entry been replaced by a directory? */ - error = -EISDIR; - if (!table->proc_handler) - goto out; + if (IS_ERR(head)) + return PTR_ERR(head); /* * At this point we know that the sysctl was not unregistered @@ -193,6 +146,11 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf, if (sysctl_perm(head->root, table, write ? MAY_WRITE : MAY_READ)) goto out; + /* if that can happen at all, it should be -EINVAL, not -EISDIR */ + error = -EINVAL; + if (!table->proc_handler) + goto out; + /* careful: calling conventions are nasty here */ res = count; error = table->proc_handler(table, write, filp, buf, &res, ppos); @@ -218,82 +176,86 @@ static ssize_t proc_sys_write(struct file *filp, const char __user *buf, static int proc_sys_fill_cache(struct file *filp, void *dirent, - filldir_t filldir, struct ctl_table *table) + filldir_t filldir, + struct ctl_table_header *head, + struct ctl_table *table) { - struct ctl_table_header *head; - struct ctl_table *child_table = NULL; struct dentry *child, *dir = filp->f_path.dentry; struct inode *inode; struct qstr qname; ino_t ino = 0; unsigned type = DT_UNKNOWN; - int ret; qname.name = table->procname; qname.len = strlen(table->procname); qname.hash = full_name_hash(qname.name, qname.len); - /* Suppress duplicates. - * Only fill a directory entry if it is the value that - * an ordinary lookup of that name returns. Hide all - * others. - * - * If we ever cache this translation in the dcache - * I should do a dcache lookup first. But for now - * it is just simpler not to. - */ - ret = 0; - child_table = do_proc_sys_lookup(dir, &qname, &head); - sysctl_head_finish(head); - if (child_table != table) - return 0; - child = d_lookup(dir, &qname); if (!child) { - struct dentry *new; - new = d_alloc(dir, &qname); - if (new) { - inode = proc_sys_make_inode(dir->d_inode, table); - if (!inode) - child = ERR_PTR(-ENOMEM); - else { - new->d_op = &proc_sys_dentry_operations; - d_add(new, inode); + child = d_alloc(dir, &qname); + if (child) { + inode = proc_sys_make_inode(dir->d_sb, head, table); + if (!inode) { + dput(child); + return -ENOMEM; + } else { + child->d_op = &proc_sys_dentry_operations; + d_add(child, inode); } - if (child) - dput(new); - else - child = new; + } else { + return -ENOMEM; } } - if (!child || IS_ERR(child) || !child->d_inode) - goto end_instantiate; inode = child->d_inode; - if (inode) { - ino = inode->i_ino; - type = inode->i_mode >> 12; - } + ino = inode->i_ino; + type = inode->i_mode >> 12; dput(child); -end_instantiate: - if (!ino) - ino= find_inode_number(dir, &qname); - if (!ino) - ino = 1; - return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type); + return !!filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type); +} + +static int scan(struct ctl_table_header *head, ctl_table *table, + unsigned long *pos, struct file *file, + void *dirent, filldir_t filldir) +{ + + for (; table->ctl_name || table->procname; table++, (*pos)++) { + int res; + + /* Can't do anything without a proc name */ + if (!table->procname) + continue; + + if (*pos < file->f_pos) + continue; + + res = proc_sys_fill_cache(file, dirent, filldir, head, table); + if (res) + return res; + + file->f_pos = *pos + 1; + } + return 0; } static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir) { - struct dentry *dentry = filp->f_dentry; + struct dentry *dentry = filp->f_path.dentry; struct inode *inode = dentry->d_inode; - struct ctl_table_header *head = NULL; - struct ctl_table *table; + struct ctl_table_header *head = grab_header(inode); + struct ctl_table *table = PROC_I(inode)->sysctl_entry; + struct ctl_table_header *h = NULL; unsigned long pos; - int ret; + int ret = -EINVAL; + + if (IS_ERR(head)) + return PTR_ERR(head); - ret = -ENOTDIR; - if (!S_ISDIR(inode->i_mode)) + if (table && !table->child) { + WARN_ON(1); goto out; + } + + table = table ? table->child : head->ctl_table; ret = 0; /* Avoid a switch here: arm builds fail with missing __cmpdi2 */ @@ -311,30 +273,17 @@ static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir) } pos = 2; - /* - Find each instance of the directory - * - Read all entries in each instance - * - Before returning an entry to user space lookup the entry - * by name and if I find a different entry don't return - * this one because it means it is a buried dup. - * For sysctl this should only happen for directory entries. - */ - for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) { - table = proc_sys_lookup_table(dentry, head->ctl_table); + ret = scan(head, table, &pos, filp, dirent, filldir); + if (ret) + goto out; - if (!table) + for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) { + if (h->attached_to != table) continue; - - for (; table->ctl_name || table->procname; table++, pos++) { - /* Can't do anything without a proc name */ - if (!table->procname) - continue; - - if (pos < filp->f_pos) - continue; - - if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0) - goto out; - filp->f_pos = pos + 1; + ret = scan(h, h->attached_by, &pos, filp, dirent, filldir); + if (ret) { + sysctl_head_finish(h); + break; } } ret = 1; @@ -349,47 +298,18 @@ static int proc_sys_permission(struct inode *inode, int mask, struct nameidata * * sysctl entries that are not writeable, * are _NOT_ writeable, capabilities or not. */ - struct ctl_table_header *head; - struct ctl_table *table; - struct dentry *dentry; - int mode; - int depth; + struct ctl_table_header *head = grab_header(inode); + struct ctl_table *table = PROC_I(inode)->sysctl_entry; int error; - head = NULL; - depth = PROC_I(inode)->fd; - - /* First check the cached permissions, in case we don't have - * enough information to lookup the sysctl table entry. - */ - error = -EACCES; - mode = inode->i_mode; - - if (current->euid == 0) - mode >>= 6; - else if (in_group_p(0)) - mode >>= 3; - - if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask) - error = 0; - - /* If we can't get a sysctl table entry the permission - * checks on the cached mode will have to be enough. - */ - if (!nd || !depth) - goto out; + if (IS_ERR(head)) + return PTR_ERR(head); - dentry = nd->path.dentry; - table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); + if (!table) /* global root - r-xr-xr-x */ + error = mask & MAY_WRITE ? -EACCES : 0; + else /* Use the permissions on the sysctl table entry */ + error = sysctl_perm(head->root, table, mask); - /* If the entry does not exist deny permission */ - error = -EACCES; - if (!table) - goto out; - - /* Use the permissions on the sysctl table entry */ - error = sysctl_perm(head->root, table, mask); -out: sysctl_head_finish(head); return error; } @@ -409,33 +329,70 @@ static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr) return error; } -/* I'm lazy and don't distinguish between files and directories, - * until access time. - */ +static int proc_sys_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + struct ctl_table_header *head = grab_header(inode); + struct ctl_table *table = PROC_I(inode)->sysctl_entry; + + if (IS_ERR(head)) + return PTR_ERR(head); + + generic_fillattr(inode, stat); + if (table) + stat->mode = (stat->mode & S_IFMT) | table->mode; + + sysctl_head_finish(head); + return 0; +} + static const struct file_operations proc_sys_file_operations = { .read = proc_sys_read, .write = proc_sys_write, +}; + +static const struct file_operations proc_sys_dir_file_operations = { .readdir = proc_sys_readdir, }; static const struct inode_operations proc_sys_inode_operations = { + .permission = proc_sys_permission, + .setattr = proc_sys_setattr, + .getattr = proc_sys_getattr, +}; + +static const struct inode_operations proc_sys_dir_operations = { .lookup = proc_sys_lookup, .permission = proc_sys_permission, .setattr = proc_sys_setattr, + .getattr = proc_sys_getattr, }; static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd) { - struct ctl_table_header *head; - struct ctl_table *table; - table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); - proc_sys_refresh_inode(dentry->d_inode, table); - sysctl_head_finish(head); - return !!table; + return !PROC_I(dentry->d_inode)->sysctl->unregistering; +} + +static int proc_sys_delete(struct dentry *dentry) +{ + return !!PROC_I(dentry->d_inode)->sysctl->unregistering; +} + +static int proc_sys_compare(struct dentry *dir, struct qstr *qstr, + struct qstr *name) +{ + struct dentry *dentry = container_of(qstr, struct dentry, d_name); + if (qstr->len != name->len) + return 1; + if (memcmp(qstr->name, name->name, name->len)) + return 1; + return !sysctl_is_seen(PROC_I(dentry->d_inode)->sysctl); } static struct dentry_operations proc_sys_dentry_operations = { .d_revalidate = proc_sys_revalidate, + .d_delete = proc_sys_delete, + .d_compare = proc_sys_compare, }; static struct proc_dir_entry *proc_sys_root; @@ -443,8 +400,8 @@ static struct proc_dir_entry *proc_sys_root; int proc_sys_init(void) { proc_sys_root = proc_mkdir("sys", NULL); - proc_sys_root->proc_iops = &proc_sys_inode_operations; - proc_sys_root->proc_fops = &proc_sys_file_operations; + proc_sys_root->proc_iops = &proc_sys_dir_operations; + proc_sys_root->proc_fops = &proc_sys_dir_file_operations; proc_sys_root->nlink = 0; return 0; } diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index f560d1705afe..fb61850d1cfc 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -282,11 +282,16 @@ union proc_op { struct task_struct *task); }; +struct ctl_table_header; +struct ctl_table; + struct proc_inode { struct pid *pid; int fd; union proc_op op; struct proc_dir_entry *pde; + struct ctl_table_header *sysctl; + struct ctl_table *sysctl_entry; struct inode vfs_inode; }; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 3f6599aeb0db..d0437f36921f 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -961,6 +961,7 @@ struct ctl_table_header; extern void sysctl_head_get(struct ctl_table_header *); extern void sysctl_head_put(struct ctl_table_header *); +extern int sysctl_is_seen(struct ctl_table_header *); extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *); extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c9a0af887033..ff5abcca5ddf 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1932,6 +1932,21 @@ void unregister_sysctl_table(struct ctl_table_header * header) spin_unlock(&sysctl_lock); } +int sysctl_is_seen(struct ctl_table_header *p) +{ + struct ctl_table_set *set = p->set; + int res; + spin_lock(&sysctl_lock); + if (p->unregistering) + res = 0; + else if (!set->is_seen) + res = 1; + else + res = set->is_seen(set); + spin_unlock(&sysctl_lock); + return res; +} + void setup_sysctl_set(struct ctl_table_set *p, struct ctl_table_set *parent, int (*is_seen)(struct ctl_table_set *)) -- cgit From e6305c43eda10ebfd2ad9e35d6e172ccc7bb3695 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 15 Jul 2008 21:03:57 -0400 Subject: [PATCH] sanitize ->permission() prototype * kill nameidata * argument; map the 3 bits in ->flags anybody cares about to new MAY_... ones and pass with the mask. * kill redundant gfs2_iop_permission() * sanitize ecryptfs_permission() * fix remaining places where ->permission() instances might barf on new MAY_... found in mask. The obvious next target in that direction is permission(9) folded fix for nfs_permission() breakage from Miklos Szeredi Signed-off-by: Al Viro --- fs/afs/internal.h | 4 +--- fs/afs/security.c | 2 +- fs/bad_inode.c | 3 +-- fs/cifs/cifsfs.c | 2 +- fs/coda/dir.c | 4 +++- fs/coda/pioctl.c | 6 ++---- fs/ecryptfs/inode.c | 17 ++--------------- fs/ext2/acl.c | 2 +- fs/ext2/acl.h | 2 +- fs/ext3/acl.c | 2 +- fs/ext3/acl.h | 2 +- fs/ext4/acl.c | 2 +- fs/ext4/acl.h | 2 +- fs/fuse/dir.c | 6 +++--- fs/gfs2/ops_inode.c | 12 +++--------- fs/hfs/inode.c | 3 +-- fs/hfsplus/inode.c | 2 +- fs/hostfs/hostfs_kern.c | 2 +- fs/jffs2/acl.c | 2 +- fs/jffs2/acl.h | 2 +- fs/jfs/acl.c | 2 +- fs/jfs/jfs_acl.h | 2 +- fs/namei.c | 23 +++++++++++++++++------ fs/nfs/dir.c | 11 +++++------ fs/ocfs2/file.c | 2 +- fs/ocfs2/file.h | 3 +-- fs/proc/base.c | 3 +-- fs/proc/proc_sysctl.c | 2 +- fs/reiserfs/xattr.c | 2 +- fs/smbfs/file.c | 4 ++-- fs/xfs/linux-2.6/xfs_iops.c | 3 +-- include/linux/coda_linux.h | 2 +- include/linux/fs.h | 5 ++++- include/linux/nfs_fs.h | 2 +- include/linux/reiserfs_xattr.h | 2 +- include/linux/shmem_fs.h | 2 +- kernel/sysctl.c | 10 +++++----- mm/shmem_acl.c | 2 +- 38 files changed, 74 insertions(+), 87 deletions(-) (limited to 'include/linux') diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 7102824ba847..3cb6920ff30b 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -469,8 +469,6 @@ extern bool afs_cm_incoming_call(struct afs_call *); extern const struct inode_operations afs_dir_inode_operations; extern const struct file_operations afs_dir_file_operations; -extern int afs_permission(struct inode *, int, struct nameidata *); - /* * file.c */ @@ -605,7 +603,7 @@ extern void afs_clear_permits(struct afs_vnode *); extern void afs_cache_permit(struct afs_vnode *, struct key *, long); extern void afs_zap_permits(struct rcu_head *); extern struct key *afs_request_key(struct afs_cell *); -extern int afs_permission(struct inode *, int, struct nameidata *); +extern int afs_permission(struct inode *, int); /* * server.c diff --git a/fs/afs/security.c b/fs/afs/security.c index 3bcbeceba1bb..3ef504370034 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c @@ -284,7 +284,7 @@ static int afs_check_permit(struct afs_vnode *vnode, struct key *key, * - AFS ACLs are attached to directories only, and a file is controlled by its * parent directory's ACL */ -int afs_permission(struct inode *inode, int mask, struct nameidata *nd) +int afs_permission(struct inode *inode, int mask) { struct afs_vnode *vnode = AFS_FS_I(inode); afs_access_t uninitialized_var(access); diff --git a/fs/bad_inode.c b/fs/bad_inode.c index f1c2ea8342f5..5f1538c03b1b 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -243,8 +243,7 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer, return -EIO; } -static int bad_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int bad_inode_permission(struct inode *inode, int mask) { return -EIO; } diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index fe5f6809cba6..1ec7076f7b24 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -267,7 +267,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) return 0; } -static int cifs_permission(struct inode *inode, int mask, struct nameidata *nd) +static int cifs_permission(struct inode *inode, int mask) { struct cifs_sb_info *cifs_sb; diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 3d2580e00a3e..c5916228243c 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -137,9 +137,11 @@ exit: } -int coda_permission(struct inode *inode, int mask, struct nameidata *nd) +int coda_permission(struct inode *inode, int mask) { int error = 0; + + mask &= MAY_READ | MAY_WRITE | MAY_EXEC; if (!mask) return 0; diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index c21a1f552a63..c38a98974fb0 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -24,8 +24,7 @@ #include /* pioctl ops */ -static int coda_ioctl_permission(struct inode *inode, int mask, - struct nameidata *nd); +static int coda_ioctl_permission(struct inode *inode, int mask); static int coda_pioctl(struct inode * inode, struct file * filp, unsigned int cmd, unsigned long user_data); @@ -42,8 +41,7 @@ const struct file_operations coda_ioctl_operations = { }; /* the coda pioctl inode ops */ -static int coda_ioctl_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int coda_ioctl_permission(struct inode *inode, int mask) { return 0; } diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index d755455e3bff..32f4228efcd5 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -830,22 +830,9 @@ out: } static int -ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd) +ecryptfs_permission(struct inode *inode, int mask) { - int rc; - - if (nd) { - struct vfsmount *vfsmnt_save = nd->path.mnt; - struct dentry *dentry_save = nd->path.dentry; - - nd->path.mnt = ecryptfs_dentry_to_lower_mnt(nd->path.dentry); - nd->path.dentry = ecryptfs_dentry_to_lower(nd->path.dentry); - rc = permission(ecryptfs_inode_to_lower(inode), mask, nd); - nd->path.mnt = vfsmnt_save; - nd->path.dentry = dentry_save; - } else - rc = permission(ecryptfs_inode_to_lower(inode), mask, NULL); - return rc; + return permission(ecryptfs_inode_to_lower(inode), mask, NULL); } /** diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index e58669e1b87c..ae8c4f850b27 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -294,7 +294,7 @@ ext2_check_acl(struct inode *inode, int mask) } int -ext2_permission(struct inode *inode, int mask, struct nameidata *nd) +ext2_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, ext2_check_acl); } diff --git a/fs/ext2/acl.h b/fs/ext2/acl.h index 0bde85bafe38..b42cf578554b 100644 --- a/fs/ext2/acl.h +++ b/fs/ext2/acl.h @@ -58,7 +58,7 @@ static inline int ext2_acl_count(size_t size) #define EXT2_ACL_NOT_CACHED ((void *)-1) /* acl.c */ -extern int ext2_permission (struct inode *, int, struct nameidata *); +extern int ext2_permission (struct inode *, int); extern int ext2_acl_chmod (struct inode *); extern int ext2_init_acl (struct inode *, struct inode *); diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c index a754d1848173..b60bb241880c 100644 --- a/fs/ext3/acl.c +++ b/fs/ext3/acl.c @@ -299,7 +299,7 @@ ext3_check_acl(struct inode *inode, int mask) } int -ext3_permission(struct inode *inode, int mask, struct nameidata *nd) +ext3_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, ext3_check_acl); } diff --git a/fs/ext3/acl.h b/fs/ext3/acl.h index 0d1e6279cbfd..42da16b8cac0 100644 --- a/fs/ext3/acl.h +++ b/fs/ext3/acl.h @@ -58,7 +58,7 @@ static inline int ext3_acl_count(size_t size) #define EXT3_ACL_NOT_CACHED ((void *)-1) /* acl.c */ -extern int ext3_permission (struct inode *, int, struct nameidata *); +extern int ext3_permission (struct inode *, int); extern int ext3_acl_chmod (struct inode *); extern int ext3_init_acl (handle_t *, struct inode *, struct inode *); diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index 3c8dab880d91..c7d04e165446 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -299,7 +299,7 @@ ext4_check_acl(struct inode *inode, int mask) } int -ext4_permission(struct inode *inode, int mask, struct nameidata *nd) +ext4_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, ext4_check_acl); } diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h index 26a5c1abf147..cd2b855a07d6 100644 --- a/fs/ext4/acl.h +++ b/fs/ext4/acl.h @@ -58,7 +58,7 @@ static inline int ext4_acl_count(size_t size) #define EXT4_ACL_NOT_CACHED ((void *)-1) /* acl.c */ -extern int ext4_permission (struct inode *, int, struct nameidata *); +extern int ext4_permission (struct inode *, int); extern int ext4_acl_chmod (struct inode *); extern int ext4_init_acl (handle_t *, struct inode *, struct inode *); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 51d0035ff07e..48a7934cb950 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -898,7 +898,7 @@ static int fuse_access(struct inode *inode, int mask) return PTR_ERR(req); memset(&inarg, 0, sizeof(inarg)); - inarg.mask = mask; + inarg.mask = mask & (MAY_READ | MAY_WRITE | MAY_EXEC); req->in.h.opcode = FUSE_ACCESS; req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; @@ -927,7 +927,7 @@ static int fuse_access(struct inode *inode, int mask) * access request is sent. Execute permission is still checked * locally based on file mode. */ -static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) +static int fuse_permission(struct inode *inode, int mask) { struct fuse_conn *fc = get_fuse_conn(inode); bool refreshed = false; @@ -962,7 +962,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) exist. So if permissions are revoked this won't be noticed immediately, only after the attribute timeout has expired */ - } else if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR))) { + } else if (mask & (MAY_ACCESS | MAY_CHDIR)) { err = fuse_access(inode, mask); } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { if (!(inode->i_mode & S_IXUGO)) { diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 1e252dfc5294..4e982532f085 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -915,12 +915,6 @@ int gfs2_permission(struct inode *inode, int mask) return error; } -static int gfs2_iop_permission(struct inode *inode, int mask, - struct nameidata *nd) -{ - return gfs2_permission(inode, mask); -} - static int setattr_size(struct inode *inode, struct iattr *attr) { struct gfs2_inode *ip = GFS2_I(inode); @@ -1150,7 +1144,7 @@ static int gfs2_removexattr(struct dentry *dentry, const char *name) } const struct inode_operations gfs2_file_iops = { - .permission = gfs2_iop_permission, + .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr, .setxattr = gfs2_setxattr, @@ -1169,7 +1163,7 @@ const struct inode_operations gfs2_dir_iops = { .rmdir = gfs2_rmdir, .mknod = gfs2_mknod, .rename = gfs2_rename, - .permission = gfs2_iop_permission, + .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr, .setxattr = gfs2_setxattr, @@ -1181,7 +1175,7 @@ const struct inode_operations gfs2_dir_iops = { const struct inode_operations gfs2_symlink_iops = { .readlink = gfs2_readlink, .follow_link = gfs2_follow_link, - .permission = gfs2_iop_permission, + .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr, .setxattr = gfs2_setxattr, diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index dc4ec640e875..aa73f3fd5dd9 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -511,8 +511,7 @@ void hfs_clear_inode(struct inode *inode) } } -static int hfs_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int hfs_permission(struct inode *inode, int mask) { if (S_ISREG(inode->i_mode) && mask & MAY_EXEC) return 0; diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index cc3b5e24339b..d4014e3044d2 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -238,7 +238,7 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev); } -static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *nd) +static int hfsplus_permission(struct inode *inode, int mask) { /* MAY_EXEC is also used for lookup, if no x bit is set allow lookup, * open_exec has the same test, so it's still not executable, if a x bit diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 5222345ddccf..d6ecabf4d231 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -822,7 +822,7 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from, return err; } -int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd) +int hostfs_permission(struct inode *ino, int desired) { char *name; int r = 0, w = 0, x = 0, err; diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 4c80404a9aba..d98713777a1b 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -314,7 +314,7 @@ static int jffs2_check_acl(struct inode *inode, int mask) return -EAGAIN; } -int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd) +int jffs2_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, jffs2_check_acl); } diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h index 0bb7f003fd80..8ca058aed384 100644 --- a/fs/jffs2/acl.h +++ b/fs/jffs2/acl.h @@ -28,7 +28,7 @@ struct jffs2_acl_header { #define JFFS2_ACL_NOT_CACHED ((void *)-1) -extern int jffs2_permission(struct inode *, int, struct nameidata *); +extern int jffs2_permission(struct inode *, int); extern int jffs2_acl_chmod(struct inode *); extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *); extern int jffs2_init_acl_post(struct inode *); diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c index 4d84bdc88299..d3e5c33665de 100644 --- a/fs/jfs/acl.c +++ b/fs/jfs/acl.c @@ -140,7 +140,7 @@ static int jfs_check_acl(struct inode *inode, int mask) return -EAGAIN; } -int jfs_permission(struct inode *inode, int mask, struct nameidata *nd) +int jfs_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, jfs_check_acl); } diff --git a/fs/jfs/jfs_acl.h b/fs/jfs/jfs_acl.h index 455fa4292045..88475f10a389 100644 --- a/fs/jfs/jfs_acl.h +++ b/fs/jfs/jfs_acl.h @@ -20,7 +20,7 @@ #ifdef CONFIG_JFS_POSIX_ACL -int jfs_permission(struct inode *, int, struct nameidata *); +int jfs_permission(struct inode *, int); int jfs_init_acl(tid_t, struct inode *, struct inode *); int jfs_setattr(struct dentry *, struct iattr *); diff --git a/fs/namei.c b/fs/namei.c index 3b26a240ade9..46af98ed136b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -185,6 +185,8 @@ int generic_permission(struct inode *inode, int mask, { umode_t mode = inode->i_mode; + mask &= MAY_READ | MAY_WRITE | MAY_EXEC; + if (current->fsuid == inode->i_uid) mode >>= 6; else { @@ -203,7 +205,7 @@ int generic_permission(struct inode *inode, int mask, /* * If the DACs are ok we don't need any capability check. */ - if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)) + if ((mask & ~mode) == 0) return 0; check_capabilities: @@ -228,7 +230,7 @@ int generic_permission(struct inode *inode, int mask, int permission(struct inode *inode, int mask, struct nameidata *nd) { - int retval, submask; + int retval; struct vfsmount *mnt = NULL; if (nd) @@ -261,9 +263,17 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) } /* Ordinary permission routines do not understand MAY_APPEND. */ - submask = mask & ~MAY_APPEND; if (inode->i_op && inode->i_op->permission) { - retval = inode->i_op->permission(inode, submask, nd); + int extra = 0; + if (nd) { + if (nd->flags & LOOKUP_ACCESS) + extra |= MAY_ACCESS; + if (nd->flags & LOOKUP_CHDIR) + extra |= MAY_CHDIR; + if (nd->flags & LOOKUP_OPEN) + extra |= MAY_OPEN; + } + retval = inode->i_op->permission(inode, mask | extra); if (!retval) { /* * Exec permission on a regular file is denied if none @@ -277,7 +287,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) return -EACCES; } } else { - retval = generic_permission(inode, submask, NULL); + retval = generic_permission(inode, mask, NULL); } if (retval) return retval; @@ -286,7 +296,8 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) if (retval) return retval; - return security_inode_permission(inode, mask, nd); + return security_inode_permission(inode, + mask & (MAY_READ|MAY_WRITE|MAY_EXEC), nd); } /** diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 28a238dab23a..74f92b717f78 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1884,7 +1884,7 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) return status; nfs_access_add_cache(inode, &cache); out: - if ((cache.mask & mask) == mask) + if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) return 0; return -EACCES; } @@ -1907,17 +1907,17 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags) return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags)); } -int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) +int nfs_permission(struct inode *inode, int mask) { struct rpc_cred *cred; int res = 0; nfs_inc_stats(inode, NFSIOS_VFSACCESS); - if (mask == 0) + if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) goto out; /* Is this sys_access() ? */ - if (nd != NULL && (nd->flags & LOOKUP_ACCESS)) + if (mask & MAY_ACCESS) goto force_lookup; switch (inode->i_mode & S_IFMT) { @@ -1926,8 +1926,7 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) case S_IFREG: /* NFSv4 has atomic_open... */ if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN) - && nd != NULL - && (nd->flags & LOOKUP_OPEN)) + && (mask & MAY_OPEN)) goto out; break; case S_IFDIR: diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index e8514e8b6ce8..be2dd95d3a1d 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1176,7 +1176,7 @@ bail: return err; } -int ocfs2_permission(struct inode *inode, int mask, struct nameidata *nd) +int ocfs2_permission(struct inode *inode, int mask) { int ret; diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h index 048ddcaf5c80..1e27b4d017ea 100644 --- a/fs/ocfs2/file.h +++ b/fs/ocfs2/file.h @@ -62,8 +62,7 @@ int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, int ocfs2_setattr(struct dentry *dentry, struct iattr *attr); int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); -int ocfs2_permission(struct inode *inode, int mask, - struct nameidata *nd); +int ocfs2_permission(struct inode *inode, int mask); int ocfs2_should_update_atime(struct inode *inode, struct vfsmount *vfsmnt); diff --git a/fs/proc/base.c b/fs/proc/base.c index 81bce6791bfc..d82d800389f6 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1859,8 +1859,7 @@ static const struct file_operations proc_fd_operations = { * /proc/pid/fd needs a special permission handler so that a process can still * access /proc/self/fd after it has executed a setuid(). */ -static int proc_fd_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int proc_fd_permission(struct inode *inode, int mask) { int rv; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index fa1ec2433e44..f9a8b892718f 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -292,7 +292,7 @@ out: return ret; } -static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd) +static int proc_sys_permission(struct inode *inode, int mask) { /* * sysctl entries that are not writeable, diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index d7c4935c1034..bb3cb5b7cdb2 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c @@ -1250,7 +1250,7 @@ static int reiserfs_check_acl(struct inode *inode, int mask) return error; } -int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd) +int reiserfs_permission(struct inode *inode, int mask) { /* * We don't do permission checks on the internal objects. diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 2294783320cb..e4f8d51a5553 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -408,7 +408,7 @@ smb_file_release(struct inode *inode, struct file * file) * privileges, so we need our own check for this. */ static int -smb_file_permission(struct inode *inode, int mask, struct nameidata *nd) +smb_file_permission(struct inode *inode, int mask) { int mode = inode->i_mode; int error = 0; @@ -417,7 +417,7 @@ smb_file_permission(struct inode *inode, int mask, struct nameidata *nd) /* Look at user permissions */ mode >>= 6; - if ((mode & 7 & mask) != mask) + if (mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) error = -EACCES; return error; } diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index 2bf287ef5489..5fc61c824bb9 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -589,8 +589,7 @@ xfs_check_acl( STATIC int xfs_vn_permission( struct inode *inode, - int mask, - struct nameidata *nd) + int mask) { return generic_permission(inode, mask, xfs_check_acl); } diff --git a/include/linux/coda_linux.h b/include/linux/coda_linux.h index 31b75311e2ca..dcc228aa335a 100644 --- a/include/linux/coda_linux.h +++ b/include/linux/coda_linux.h @@ -37,7 +37,7 @@ extern const struct file_operations coda_ioctl_operations; /* operations shared over more than one file */ int coda_open(struct inode *i, struct file *f); int coda_release(struct inode *i, struct file *f); -int coda_permission(struct inode *inode, int mask, struct nameidata *nd); +int coda_permission(struct inode *inode, int mask); int coda_revalidate_inode(struct dentry *); int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *); int coda_setattr(struct dentry *, struct iattr *); diff --git a/include/linux/fs.h b/include/linux/fs.h index 7721a2ac9c0e..6c923c9b79bc 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -60,6 +60,9 @@ extern int dir_notify_enable; #define MAY_WRITE 2 #define MAY_READ 4 #define MAY_APPEND 8 +#define MAY_ACCESS 16 +#define MAY_CHDIR 32 +#define MAY_OPEN 64 #define FMODE_READ 1 #define FMODE_WRITE 2 @@ -1272,7 +1275,7 @@ struct inode_operations { void * (*follow_link) (struct dentry *, struct nameidata *); void (*put_link) (struct dentry *, struct nameidata *, void *); void (*truncate) (struct inode *); - int (*permission) (struct inode *, int, struct nameidata *); + int (*permission) (struct inode *, int); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *); int (*setxattr) (struct dentry *, const char *,const void *,size_t,int); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 29d261918734..f08f9ca602af 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -332,7 +332,7 @@ extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr); extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr); extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); -extern int nfs_permission(struct inode *, int, struct nameidata *); +extern int nfs_permission(struct inode *, int); extern int nfs_open(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *); extern int nfs_attribute_timeout(struct inode *inode); diff --git a/include/linux/reiserfs_xattr.h b/include/linux/reiserfs_xattr.h index 66a96814d614..af135ae895db 100644 --- a/include/linux/reiserfs_xattr.h +++ b/include/linux/reiserfs_xattr.h @@ -55,7 +55,7 @@ int reiserfs_removexattr(struct dentry *dentry, const char *name); int reiserfs_delete_xattrs(struct inode *inode); int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs); int reiserfs_xattr_init(struct super_block *sb, int mount_flags); -int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd); +int reiserfs_permission(struct inode *inode, int mask); int reiserfs_xattr_del(struct inode *, const char *); int reiserfs_xattr_get(const struct inode *, const char *, void *, size_t); diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index f2d12d5a21b8..fd83f2584b15 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -43,7 +43,7 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) } #ifdef CONFIG_TMPFS_POSIX_ACL -int shmem_permission(struct inode *, int, struct nameidata *); +int shmem_permission(struct inode *, int); int shmem_acl_init(struct inode *, struct inode *); void shmem_acl_destroy_inode(struct inode *); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index ff5abcca5ddf..911d846f0503 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1516,9 +1516,9 @@ static int do_sysctl_strategy(struct ctl_table_root *root, int op = 0, rc; if (oldval) - op |= 004; + op |= MAY_READ; if (newval) - op |= 002; + op |= MAY_WRITE; if (sysctl_perm(root, table, op)) return -EPERM; @@ -1560,7 +1560,7 @@ repeat: if (n == table->ctl_name) { int error; if (table->child) { - if (sysctl_perm(root, table, 001)) + if (sysctl_perm(root, table, MAY_EXEC)) return -EPERM; name++; nlen--; @@ -1635,7 +1635,7 @@ static int test_perm(int mode, int op) mode >>= 6; else if (in_egroup_p(0)) mode >>= 3; - if ((mode & op & 0007) == op) + if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0) return 0; return -EACCES; } @@ -1645,7 +1645,7 @@ int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op) int error; int mode; - error = security_sysctl(table, op); + error = security_sysctl(table, op & (MAY_READ | MAY_WRITE | MAY_EXEC)); if (error) return error; diff --git a/mm/shmem_acl.c b/mm/shmem_acl.c index f5664c5b9eb1..8e5aadd7dcd6 100644 --- a/mm/shmem_acl.c +++ b/mm/shmem_acl.c @@ -191,7 +191,7 @@ shmem_check_acl(struct inode *inode, int mask) * shmem_permission - permission() inode operation */ int -shmem_permission(struct inode *inode, int mask, struct nameidata *nd) +shmem_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, shmem_check_acl); } -- cgit From 2f1936b87783a3a56c9441b27b9ba7a747f11e8e Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 24 Jun 2008 16:50:14 +0200 Subject: [patch 3/5] vfs: change remove_suid() to file_remove_suid() All calls to remove_suid() are made with a file pointer, because (similarly to file_update_time) it is called when the file is written. Clean up callers by passing in a file instead of a dentry. Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 2 +- fs/ntfs/file.c | 2 +- fs/splice.c | 4 ++-- fs/xfs/linux-2.6/xfs_lrw.c | 2 +- include/linux/fs.h | 2 +- mm/filemap.c | 7 ++++--- mm/filemap_xip.c | 2 +- 7 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 67ff2c6a8f63..2bada6bbc317 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -893,7 +893,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov, if (count == 0) goto out; - err = remove_suid(file->f_path.dentry); + err = file_remove_suid(file); if (err) goto out; diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 3c5550cd11d6..d020866d4232 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -2118,7 +2118,7 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb, goto out; if (!count) goto out; - err = remove_suid(file->f_path.dentry); + err = file_remove_suid(file); if (err) goto out; file_update_time(file); diff --git a/fs/splice.c b/fs/splice.c index 47dc1a445d1f..b30311ba8af6 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -772,7 +772,7 @@ generic_file_splice_write_nolock(struct pipe_inode_info *pipe, struct file *out, ssize_t ret; int err; - err = remove_suid(out->f_path.dentry); + err = file_remove_suid(out); if (unlikely(err)) return err; @@ -830,7 +830,7 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out, ssize_t ret; inode_double_lock(inode, pipe->inode); - ret = remove_suid(out->f_path.dentry); + ret = file_remove_suid(out); if (likely(!ret)) ret = __splice_from_pipe(pipe, &sd, pipe_to_file); inode_double_unlock(inode, pipe->inode); diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c index 5e3b57516ec7..82333b3e118e 100644 --- a/fs/xfs/linux-2.6/xfs_lrw.c +++ b/fs/xfs/linux-2.6/xfs_lrw.c @@ -711,7 +711,7 @@ start: !capable(CAP_FSETID)) { error = xfs_write_clear_setuid(xip); if (likely(!error)) - error = -remove_suid(file->f_path.dentry); + error = -file_remove_suid(file); if (unlikely(error)) { goto out_unlock_internal; } diff --git a/include/linux/fs.h b/include/linux/fs.h index 6c923c9b79bc..1a3546e69f9e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1834,7 +1834,7 @@ extern void clear_inode(struct inode *); extern void destroy_inode(struct inode *); extern struct inode *new_inode(struct super_block *); extern int should_remove_suid(struct dentry *); -extern int remove_suid(struct dentry *); +extern int file_remove_suid(struct file *); extern void __insert_inode_hash(struct inode *, unsigned long hashval); extern void remove_inode_hash(struct inode *); diff --git a/mm/filemap.c b/mm/filemap.c index 2ed8b0389c51..5de7633e1dbe 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1758,8 +1758,9 @@ static int __remove_suid(struct dentry *dentry, int kill) return notify_change(dentry, &newattrs); } -int remove_suid(struct dentry *dentry) +int file_remove_suid(struct file *file) { + struct dentry *dentry = file->f_path.dentry; int killsuid = should_remove_suid(dentry); int killpriv = security_inode_need_killpriv(dentry); int error = 0; @@ -1773,7 +1774,7 @@ int remove_suid(struct dentry *dentry) return error; } -EXPORT_SYMBOL(remove_suid); +EXPORT_SYMBOL(file_remove_suid); static size_t __iovec_copy_from_user_inatomic(char *vaddr, const struct iovec *iov, size_t base, size_t bytes) @@ -2529,7 +2530,7 @@ __generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, if (count == 0) goto out; - err = remove_suid(file->f_path.dentry); + err = file_remove_suid(file); if (err) goto out; diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 3e744abcce9d..98a3f31ccd6a 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -380,7 +380,7 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len, if (count == 0) goto out_backing; - ret = remove_suid(filp->f_path.dentry); + ret = file_remove_suid(filp); if (ret) goto out_backing; -- cgit From db2e747b14991a4c6a5c98b0e5f552a193237c03 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 24 Jun 2008 16:50:16 +0200 Subject: [patch 5/5] vfs: remove mode parameter from vfs_symlink() Remove the unused mode parameter from vfs_symlink and callers. Thanks to Tetsuo Handa for noticing. CC: Tetsuo Handa Signed-off-by: Miklos Szeredi --- fs/ecryptfs/inode.c | 4 +--- fs/namei.c | 4 ++-- fs/nfsd/vfs.c | 10 ++-------- include/linux/fs.h | 2 +- 4 files changed, 6 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 32f4228efcd5..f25caf2b0887 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -465,7 +465,6 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; - umode_t mode; char *encoded_symname; int encoded_symlen; struct ecryptfs_crypt_stat *crypt_stat = NULL; @@ -473,7 +472,6 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); - mode = S_IALLUGO; encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname, strlen(symname), &encoded_symname); @@ -482,7 +480,7 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, goto out_lock; } rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, - encoded_symname, mode); + encoded_symname); kfree(encoded_symname); if (rc || !lower_dentry->d_inode) goto out_lock; diff --git a/fs/namei.c b/fs/namei.c index 3b67be7631dc..ae0e56fdb742 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2434,7 +2434,7 @@ asmlinkage long sys_unlink(const char __user *pathname) return do_unlinkat(AT_FDCWD, pathname); } -int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode) +int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) { int error = may_create(dir, dentry, NULL); @@ -2483,7 +2483,7 @@ asmlinkage long sys_symlinkat(const char __user *oldname, error = mnt_want_write(nd.path.mnt); if (error) goto out_dput; - error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO); + error = vfs_symlink(nd.path.dentry->d_inode, dentry, from); mnt_drop_write(nd.path.mnt); out_dput: dput(dentry); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 0f4481e0502d..ad1ad59e3742 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1516,7 +1516,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, struct dentry *dentry, *dnew; __be32 err, cerr; int host_err; - umode_t mode; err = nfserr_noent; if (!flen || !plen) @@ -1535,11 +1534,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, if (IS_ERR(dnew)) goto out_nfserr; - mode = S_IALLUGO; - /* Only the MODE ATTRibute is even vaguely meaningful */ - if (iap && (iap->ia_valid & ATTR_MODE)) - mode = iap->ia_mode & S_IALLUGO; - host_err = mnt_want_write(fhp->fh_export->ex_path.mnt); if (host_err) goto out_nfserr; @@ -1551,11 +1545,11 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, else { strncpy(path_alloced, path, plen); path_alloced[plen] = 0; - host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced, mode); + host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced); kfree(path_alloced); } } else - host_err = vfs_symlink(dentry->d_inode, dnew, path, mode); + host_err = vfs_symlink(dentry->d_inode, dnew, path); if (!host_err) { if (EX_ISSYNC(fhp->fh_export)) diff --git a/include/linux/fs.h b/include/linux/fs.h index 1a3546e69f9e..25998e803fc2 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1139,7 +1139,7 @@ extern int vfs_permission(struct nameidata *, int); extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *); extern int vfs_mkdir(struct inode *, struct dentry *, int); extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t); -extern int vfs_symlink(struct inode *, struct dentry *, const char *, int); +extern int vfs_symlink(struct inode *, struct dentry *, const char *); extern int vfs_link(struct dentry *, struct inode *, struct dentry *); extern int vfs_rmdir(struct inode *, struct dentry *); extern int vfs_unlink(struct inode *, struct dentry *); -- cgit From 8bb79224b87aab92071e94d46e70bd160d89bf34 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 16 Jul 2008 09:51:03 -0400 Subject: [PATCH] permission checks for chdir need special treatment only on the last step ... so we ought to pass MAY_CHDIR to vfs_permission() instead of having it triggered on every step of preceding pathname resolution. LOOKUP_CHDIR is killed by that. Signed-off-by: Al Viro --- fs/namei.c | 2 -- fs/open.c | 5 ++--- include/linux/namei.h | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/namei.c b/fs/namei.c index ae0e56fdb742..6c76e1ee9c45 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -268,8 +268,6 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) if (nd) { if (nd->flags & LOOKUP_ACCESS) extra |= MAY_ACCESS; - if (nd->flags & LOOKUP_CHDIR) - extra |= MAY_CHDIR; if (nd->flags & LOOKUP_OPEN) extra |= MAY_OPEN; } diff --git a/fs/open.c b/fs/open.c index b2e4c93aed03..8e02d42bfe44 100644 --- a/fs/open.c +++ b/fs/open.c @@ -501,12 +501,11 @@ asmlinkage long sys_chdir(const char __user * filename) struct nameidata nd; int error; - error = __user_walk(filename, - LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd); + error = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd); if (error) goto out; - error = vfs_permission(&nd, MAY_EXEC); + error = vfs_permission(&nd, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; diff --git a/include/linux/namei.h b/include/linux/namei.h index 24d88e98a626..3cf62d26d493 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -55,7 +55,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; #define LOOKUP_OPEN (0x0100) #define LOOKUP_CREATE (0x0200) #define LOOKUP_ACCESS (0x0400) -#define LOOKUP_CHDIR (0x0800) extern int __user_walk(const char __user *, unsigned, struct nameidata *); extern int __user_walk_fd(int dfd, const char __user *, unsigned, struct nameidata *); -- cgit From 7f2da1e7d0330395e5e9e350b879b98a1ea495df Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 10 May 2008 20:44:54 -0400 Subject: [PATCH] kill altroot long overdue... Signed-off-by: Al Viro --- fs/namei.c | 89 +------------------------------------------ fs/namespace.c | 8 +--- fs/open.c | 3 +- include/asm-alpha/namei.h | 17 --------- include/asm-arm/namei.h | 25 ------------ include/asm-avr32/namei.h | 7 ---- include/asm-blackfin/namei.h | 19 --------- include/asm-cris/namei.h | 17 --------- include/asm-frv/namei.h | 18 --------- include/asm-h8300/namei.h | 17 --------- include/asm-ia64/namei.h | 25 ------------ include/asm-m32r/namei.h | 17 --------- include/asm-m68k/namei.h | 17 --------- include/asm-m68knommu/namei.h | 1 - include/asm-mips/namei.h | 11 ------ include/asm-mn10300/namei.h | 22 ----------- include/asm-parisc/namei.h | 17 --------- include/asm-powerpc/namei.h | 20 ---------- include/asm-s390/namei.h | 21 ---------- include/asm-sh/namei.h | 17 --------- include/asm-sparc/namei.h | 8 ---- include/asm-sparc64/namei.h | 1 - include/asm-um/namei.h | 6 --- include/asm-v850/namei.h | 17 --------- include/asm-x86/namei.h | 11 ------ include/asm-xtensa/namei.h | 26 ------------- include/linux/fs_struct.h | 3 +- include/linux/namei.h | 1 - kernel/exec_domain.c | 1 - kernel/exit.c | 2 - kernel/fork.c | 7 ---- 31 files changed, 5 insertions(+), 466 deletions(-) delete mode 100644 include/asm-alpha/namei.h delete mode 100644 include/asm-arm/namei.h delete mode 100644 include/asm-avr32/namei.h delete mode 100644 include/asm-blackfin/namei.h delete mode 100644 include/asm-cris/namei.h delete mode 100644 include/asm-frv/namei.h delete mode 100644 include/asm-h8300/namei.h delete mode 100644 include/asm-ia64/namei.h delete mode 100644 include/asm-m32r/namei.h delete mode 100644 include/asm-m68k/namei.h delete mode 100644 include/asm-m68knommu/namei.h delete mode 100644 include/asm-mips/namei.h delete mode 100644 include/asm-mn10300/namei.h delete mode 100644 include/asm-parisc/namei.h delete mode 100644 include/asm-powerpc/namei.h delete mode 100644 include/asm-s390/namei.h delete mode 100644 include/asm-sh/namei.h delete mode 100644 include/asm-sparc/namei.h delete mode 100644 include/asm-sparc64/namei.h delete mode 100644 include/asm-um/namei.h delete mode 100644 include/asm-v850/namei.h delete mode 100644 include/asm-x86/namei.h delete mode 100644 include/asm-xtensa/namei.h (limited to 'include/linux') diff --git a/fs/namei.c b/fs/namei.c index 6c76e1ee9c45..095818089ac1 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) @@ -562,27 +561,16 @@ out_unlock: return result; } -static int __emul_lookup_dentry(const char *, struct nameidata *); - /* SMP-safe */ -static __always_inline int +static __always_inline void walk_init_root(const char *name, struct nameidata *nd) { struct fs_struct *fs = current->fs; read_lock(&fs->lock); - if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) { - nd->path = fs->altroot; - path_get(&fs->altroot); - read_unlock(&fs->lock); - if (__emul_lookup_dentry(name,nd)) - return 0; - read_lock(&fs->lock); - } nd->path = fs->root; path_get(&fs->root); read_unlock(&fs->lock); - return 1; } /* @@ -623,12 +611,9 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l if (*link == '/') { path_put(&nd->path); - if (!walk_init_root(link, nd)) - /* weird __emul_prefix() stuff did it */ - goto out; + walk_init_root(link, nd); } res = link_path_walk(link, nd); -out: if (nd->depth || res || nd->last_type!=LAST_NORM) return res; /* @@ -1077,67 +1062,6 @@ static int path_walk(const char *name, struct nameidata *nd) return link_path_walk(name, nd); } -/* - * SMP-safe: Returns 1 and nd will have valid dentry and mnt, if - * everything is done. Returns 0 and drops input nd, if lookup failed; - */ -static int __emul_lookup_dentry(const char *name, struct nameidata *nd) -{ - if (path_walk(name, nd)) - return 0; /* something went wrong... */ - - if (!nd->path.dentry->d_inode || - S_ISDIR(nd->path.dentry->d_inode->i_mode)) { - struct path old_path = nd->path; - struct qstr last = nd->last; - int last_type = nd->last_type; - struct fs_struct *fs = current->fs; - - /* - * NAME was not found in alternate root or it's a directory. - * Try to find it in the normal root: - */ - nd->last_type = LAST_ROOT; - read_lock(&fs->lock); - nd->path = fs->root; - path_get(&fs->root); - read_unlock(&fs->lock); - if (path_walk(name, nd) == 0) { - if (nd->path.dentry->d_inode) { - path_put(&old_path); - return 1; - } - path_put(&nd->path); - } - nd->path = old_path; - nd->last = last; - nd->last_type = last_type; - } - return 1; -} - -void set_fs_altroot(void) -{ - char *emul = __emul_prefix(); - struct nameidata nd; - struct path path = {}, old_path; - int err; - struct fs_struct *fs = current->fs; - - if (!emul) - goto set_it; - err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd); - if (!err) - path = nd.path; -set_it: - write_lock(&fs->lock); - old_path = fs->altroot; - fs->altroot = path; - write_unlock(&fs->lock); - if (old_path.dentry) - path_put(&old_path); -} - /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ static int do_path_lookup(int dfd, const char *name, unsigned int flags, struct nameidata *nd) @@ -1153,14 +1077,6 @@ static int do_path_lookup(int dfd, const char *name, if (*name=='/') { read_lock(&fs->lock); - if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) { - nd->path = fs->altroot; - path_get(&fs->altroot); - read_unlock(&fs->lock); - if (__emul_lookup_dentry(name,nd)) - goto out; /* found in altroot */ - read_lock(&fs->lock); - } nd->path = fs->root; path_get(&fs->root); read_unlock(&fs->lock); @@ -1194,7 +1110,6 @@ static int do_path_lookup(int dfd, const char *name, } retval = path_walk(name, nd); -out: if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && nd->path.dentry->d_inode)) audit_inode(name, nd->path.dentry); diff --git a/fs/namespace.c b/fs/namespace.c index f30b11e2240e..c4fcf48acef8 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1972,7 +1972,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, struct fs_struct *fs) { struct mnt_namespace *new_ns; - struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL; + struct vfsmount *rootmnt = NULL, *pwdmnt = NULL; struct vfsmount *p, *q; new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL); @@ -2015,10 +2015,6 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, pwdmnt = p; fs->pwd.mnt = mntget(q); } - if (p == fs->altroot.mnt) { - altrootmnt = p; - fs->altroot.mnt = mntget(q); - } } p = next_mnt(p, mnt_ns->root); q = next_mnt(q, new_ns->root); @@ -2029,8 +2025,6 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns, mntput(rootmnt); if (pwdmnt) mntput(pwdmnt); - if (altrootmnt) - mntput(altrootmnt); return new_ns; } diff --git a/fs/open.c b/fs/open.c index 8e02d42bfe44..d3a2a00f52dc 100644 --- a/fs/open.c +++ b/fs/open.c @@ -548,7 +548,7 @@ asmlinkage long sys_chroot(const char __user * filename) struct nameidata nd; int error; - error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); + error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd); if (error) goto out; @@ -561,7 +561,6 @@ asmlinkage long sys_chroot(const char __user * filename) goto dput_and_out; set_fs_root(current->fs, &nd.path); - set_fs_altroot(); error = 0; dput_and_out: path_put(&nd.path); diff --git a/include/asm-alpha/namei.h b/include/asm-alpha/namei.h deleted file mode 100644 index 5cc9bb39499d..000000000000 --- a/include/asm-alpha/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* $Id: namei.h,v 1.1 1996/12/13 14:48:21 jj Exp $ - * linux/include/asm-alpha/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __ALPHA_NAMEI_H -#define __ALPHA_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __ALPHA_NAMEI_H */ diff --git a/include/asm-arm/namei.h b/include/asm-arm/namei.h deleted file mode 100644 index a402d3b9d0f7..000000000000 --- a/include/asm-arm/namei.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * linux/include/asm-arm/namei.h - * - * Routines to handle famous /usr/gnemul - * Derived from the Sparc version of this file - * - * Included from linux/fs/namei.c - */ - -#ifndef __ASMARM_NAMEI_H -#define __ASMARM_NAMEI_H - -#define ARM_BSD_EMUL "usr/gnemul/bsd/" - -static inline char *__emul_prefix(void) -{ - switch (current->personality) { - case PER_BSD: - return ARM_BSD_EMUL; - default: - return NULL; - } -} - -#endif /* __ASMARM_NAMEI_H */ diff --git a/include/asm-avr32/namei.h b/include/asm-avr32/namei.h deleted file mode 100644 index f0a26de06cab..000000000000 --- a/include/asm-avr32/namei.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __ASM_AVR32_NAMEI_H -#define __ASM_AVR32_NAMEI_H - -/* This dummy routine may be changed to something useful */ -#define __emul_prefix() NULL - -#endif /* __ASM_AVR32_NAMEI_H */ diff --git a/include/asm-blackfin/namei.h b/include/asm-blackfin/namei.h deleted file mode 100644 index 8b89a2d65cb4..000000000000 --- a/include/asm-blackfin/namei.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * linux/include/asm/namei.h - * - * Included from linux/fs/namei.c - * - * Changes made by Lineo Inc. May 2001 - */ - -#ifndef __BFIN_NAMEI_H -#define __BFIN_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif diff --git a/include/asm-cris/namei.h b/include/asm-cris/namei.h deleted file mode 100644 index 8a3be7a6d9f6..000000000000 --- a/include/asm-cris/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* $Id: namei.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $ - * linux/include/asm-cris/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __CRIS_NAMEI_H -#define __CRIS_NAMEI_H - -/* used to find file-system prefixes for doing emulations - * see for example asm-sparc/namei.h - * we don't use it... - */ - -#define __emul_prefix() NULL - -#endif /* __CRIS_NAMEI_H */ diff --git a/include/asm-frv/namei.h b/include/asm-frv/namei.h deleted file mode 100644 index 4ea57171d951..000000000000 --- a/include/asm-frv/namei.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * include/asm-frv/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __ASM_NAMEI_H -#define __ASM_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif - diff --git a/include/asm-h8300/namei.h b/include/asm-h8300/namei.h deleted file mode 100644 index ab6f196db6e0..000000000000 --- a/include/asm-h8300/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * linux/include/asm-h8300/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __H8300_NAMEI_H -#define __H8300_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif diff --git a/include/asm-ia64/namei.h b/include/asm-ia64/namei.h deleted file mode 100644 index 78e768079083..000000000000 --- a/include/asm-ia64/namei.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _ASM_IA64_NAMEI_H -#define _ASM_IA64_NAMEI_H - -/* - * Modified 1998, 1999, 2001 - * David Mosberger-Tang , Hewlett-Packard Co - */ - -#include -#include - -#define EMUL_PREFIX_LINUX_IA32 "/emul/ia32-linux/" - -static inline char * -__emul_prefix (void) -{ - switch (current->personality) { - case PER_LINUX32: - return EMUL_PREFIX_LINUX_IA32; - default: - return NULL; - } -} - -#endif /* _ASM_IA64_NAMEI_H */ diff --git a/include/asm-m32r/namei.h b/include/asm-m32r/namei.h deleted file mode 100644 index 210f8056b805..000000000000 --- a/include/asm-m32r/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _ASM_M32R_NAMEI_H -#define _ASM_M32R_NAMEI_H - -/* - * linux/include/asm-m32r/namei.h - * - * Included from linux/fs/namei.c - */ - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* _ASM_M32R_NAMEI_H */ diff --git a/include/asm-m68k/namei.h b/include/asm-m68k/namei.h deleted file mode 100644 index f33f243b644a..000000000000 --- a/include/asm-m68k/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * linux/include/asm-m68k/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __M68K_NAMEI_H -#define __M68K_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif diff --git a/include/asm-m68knommu/namei.h b/include/asm-m68knommu/namei.h deleted file mode 100644 index 31a85d27b931..000000000000 --- a/include/asm-m68knommu/namei.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-mips/namei.h b/include/asm-mips/namei.h deleted file mode 100644 index a6605a752469..000000000000 --- a/include/asm-mips/namei.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _ASM_NAMEI_H -#define _ASM_NAMEI_H - -/* - * This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - */ - -#define __emul_prefix() NULL - -#endif /* _ASM_NAMEI_H */ diff --git a/include/asm-mn10300/namei.h b/include/asm-mn10300/namei.h deleted file mode 100644 index bd9ce94aeb65..000000000000 --- a/include/asm-mn10300/namei.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Emulation stuff - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. - */ - -#ifndef _ASM_NAMEI_H -#define _ASM_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* _ASM_NAMEI_H */ diff --git a/include/asm-parisc/namei.h b/include/asm-parisc/namei.h deleted file mode 100644 index 8d29b3d9fb33..000000000000 --- a/include/asm-parisc/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* $Id: namei.h,v 1.1 1996/12/13 14:48:21 jj Exp $ - * linux/include/asm-parisc/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __PARISC_NAMEI_H -#define __PARISC_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __PARISC_NAMEI_H */ diff --git a/include/asm-powerpc/namei.h b/include/asm-powerpc/namei.h deleted file mode 100644 index 657443474a6a..000000000000 --- a/include/asm-powerpc/namei.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _ASM_POWERPC_NAMEI_H -#define _ASM_POWERPC_NAMEI_H - -#ifdef __KERNEL__ - -/* - * Adapted from include/asm-alpha/namei.h - * - * Included from fs/namei.c - */ - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __KERNEL__ */ -#endif /* _ASM_POWERPC_NAMEI_H */ diff --git a/include/asm-s390/namei.h b/include/asm-s390/namei.h deleted file mode 100644 index 3e286bdde4b0..000000000000 --- a/include/asm-s390/namei.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * include/asm-s390/namei.h - * - * S390 version - * - * Derived from "include/asm-i386/namei.h" - * - * Included from linux/fs/namei.c - */ - -#ifndef __S390_NAMEI_H -#define __S390_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __S390_NAMEI_H */ diff --git a/include/asm-sh/namei.h b/include/asm-sh/namei.h deleted file mode 100644 index 338a5d947143..000000000000 --- a/include/asm-sh/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* $Id: namei.h,v 1.3 2000/07/04 06:24:49 gniibe Exp $ - * linux/include/asm-sh/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __ASM_SH_NAMEI_H -#define __ASM_SH_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __ASM_SH_NAMEI_H */ diff --git a/include/asm-sparc/namei.h b/include/asm-sparc/namei.h deleted file mode 100644 index eff944b8e321..000000000000 --- a/include/asm-sparc/namei.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ___ASM_SPARC_NAMEI_H -#define ___ASM_SPARC_NAMEI_H -#if defined(__sparc__) && defined(__arch64__) -#include -#else -#include -#endif -#endif diff --git a/include/asm-sparc64/namei.h b/include/asm-sparc64/namei.h deleted file mode 100644 index 1344a910ba2f..000000000000 --- a/include/asm-sparc64/namei.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/asm-um/namei.h b/include/asm-um/namei.h deleted file mode 100644 index 002984d5bc85..000000000000 --- a/include/asm-um/namei.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __UM_NAMEI_H -#define __UM_NAMEI_H - -#include "asm/arch/namei.h" - -#endif diff --git a/include/asm-v850/namei.h b/include/asm-v850/namei.h deleted file mode 100644 index ee8339b23843..000000000000 --- a/include/asm-v850/namei.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * linux/include/asm-v850/namei.h - * - * Included from linux/fs/namei.c - */ - -#ifndef __V850_NAMEI_H__ -#define __V850_NAMEI_H__ - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __V850_NAMEI_H__ */ diff --git a/include/asm-x86/namei.h b/include/asm-x86/namei.h deleted file mode 100644 index 415ef5d9550e..000000000000 --- a/include/asm-x86/namei.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _ASM_X86_NAMEI_H -#define _ASM_X86_NAMEI_H - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* _ASM_X86_NAMEI_H */ diff --git a/include/asm-xtensa/namei.h b/include/asm-xtensa/namei.h deleted file mode 100644 index 3fdff039d27d..000000000000 --- a/include/asm-xtensa/namei.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * include/asm-xtensa/namei.h - * - * Included from linux/fs/namei.c - * - * 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 - * for more details. - * - * Copyright (C) 2001 - 2005 Tensilica Inc. - */ - -#ifndef _XTENSA_NAMEI_H -#define _XTENSA_NAMEI_H - -#ifdef __KERNEL__ - -/* This dummy routine maybe changed to something useful - * for /usr/gnemul/ emulation stuff. - * Look at asm-sparc/namei.h for details. - */ - -#define __emul_prefix() NULL - -#endif /* __KERNEL__ */ -#endif /* _XTENSA_NAMEI_H */ diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h index 282f54219129..9e5a06e78d02 100644 --- a/include/linux/fs_struct.h +++ b/include/linux/fs_struct.h @@ -7,7 +7,7 @@ struct fs_struct { atomic_t count; rwlock_t lock; int umask; - struct path root, pwd, altroot; + struct path root, pwd; }; #define INIT_FS { \ @@ -19,7 +19,6 @@ struct fs_struct { extern struct kmem_cache *fs_cachep; extern void exit_fs(struct task_struct *); -extern void set_fs_altroot(void); extern void set_fs_root(struct fs_struct *, struct path *); extern void set_fs_pwd(struct fs_struct *, struct path *); extern struct fs_struct *copy_fs_struct(struct fs_struct *); diff --git a/include/linux/namei.h b/include/linux/namei.h index 3cf62d26d493..768773d57857 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -47,7 +47,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; #define LOOKUP_DIRECTORY 2 #define LOOKUP_CONTINUE 4 #define LOOKUP_PARENT 16 -#define LOOKUP_NOALT 32 #define LOOKUP_REVAL 64 /* * Intent data diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c index c1ef192aa655..0d407e886735 100644 --- a/kernel/exec_domain.c +++ b/kernel/exec_domain.c @@ -168,7 +168,6 @@ __set_personality(u_long personality) current->personality = personality; oep = current_thread_info()->exec_domain; current_thread_info()->exec_domain = ep; - set_fs_altroot(); module_put(oep->module); return 0; diff --git a/kernel/exit.c b/kernel/exit.c index 6cdf60712bd2..0caf590548a0 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -565,8 +565,6 @@ void put_fs_struct(struct fs_struct *fs) if (atomic_dec_and_test(&fs->count)) { path_put(&fs->root); path_put(&fs->pwd); - if (fs->altroot.dentry) - path_put(&fs->altroot); kmem_cache_free(fs_cachep, fs); } } diff --git a/kernel/fork.c b/kernel/fork.c index abb3ed6298f6..5e050c1317c4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -657,13 +657,6 @@ static struct fs_struct *__copy_fs_struct(struct fs_struct *old) path_get(&old->root); fs->pwd = old->pwd; path_get(&old->pwd); - if (old->altroot.dentry) { - fs->altroot = old->altroot; - path_get(&old->altroot); - } else { - fs->altroot.mnt = NULL; - fs->altroot.dentry = NULL; - } read_unlock(&old->lock); } return fs; -- cgit From a110343f0d6d41f68b7cf8c00b57a3172c67f816 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Jul 2008 09:19:08 -0400 Subject: [PATCH] fix MAY_CHDIR/MAY_ACCESS/LOOKUP_ACCESS mess * MAY_CHDIR is redundant - it's an equivalent of MAY_ACCESS * MAY_ACCESS on fuse should affect only the last step of pathname resolution * fchdir() and chroot() should pass MAY_ACCESS, for the same reason why chdir() needs that. * now that we pass MAY_ACCESS explicitly in all cases, LOOKUP_ACCESS can be removed; it has no business being in nameidata. Signed-off-by: Al Viro --- fs/fuse/dir.c | 2 +- fs/namei.c | 2 -- fs/open.c | 10 +++++----- include/linux/fs.h | 3 +-- include/linux/namei.h | 1 - 5 files changed, 7 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 48a7934cb950..fd03330cadeb 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -962,7 +962,7 @@ static int fuse_permission(struct inode *inode, int mask) exist. So if permissions are revoked this won't be noticed immediately, only after the attribute timeout has expired */ - } else if (mask & (MAY_ACCESS | MAY_CHDIR)) { + } else if (mask & MAY_ACCESS) { err = fuse_access(inode, mask); } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { if (!(inode->i_mode & S_IXUGO)) { diff --git a/fs/namei.c b/fs/namei.c index 095818089ac1..33dcaf025c49 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -265,8 +265,6 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) if (inode->i_op && inode->i_op->permission) { int extra = 0; if (nd) { - if (nd->flags & LOOKUP_ACCESS) - extra |= MAY_ACCESS; if (nd->flags & LOOKUP_OPEN) extra |= MAY_OPEN; } diff --git a/fs/open.c b/fs/open.c index d3a2a00f52dc..3317e1909b2c 100644 --- a/fs/open.c +++ b/fs/open.c @@ -457,11 +457,11 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) old_cap = cap_set_effective(current->cap_permitted); } - res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd); + res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); if (res) goto out; - res = vfs_permission(&nd, mode); + res = vfs_permission(&nd, mode | MAY_ACCESS); /* SuS v2 requires we report a read only fs too */ if(res || !(mode & S_IWOTH) || special_file(nd.path.dentry->d_inode->i_mode)) @@ -505,7 +505,7 @@ asmlinkage long sys_chdir(const char __user * filename) if (error) goto out; - error = vfs_permission(&nd, MAY_EXEC | MAY_CHDIR); + error = vfs_permission(&nd, MAY_EXEC | MAY_ACCESS); if (error) goto dput_and_out; @@ -534,7 +534,7 @@ asmlinkage long sys_fchdir(unsigned int fd) if (!S_ISDIR(inode->i_mode)) goto out_putf; - error = file_permission(file, MAY_EXEC); + error = file_permission(file, MAY_EXEC | MAY_ACCESS); if (!error) set_fs_pwd(current->fs, &file->f_path); out_putf: @@ -552,7 +552,7 @@ asmlinkage long sys_chroot(const char __user * filename) if (error) goto out; - error = vfs_permission(&nd, MAY_EXEC); + error = vfs_permission(&nd, MAY_EXEC | MAY_ACCESS); if (error) goto dput_and_out; diff --git a/include/linux/fs.h b/include/linux/fs.h index 25998e803fc2..d8721e818b45 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -61,8 +61,7 @@ extern int dir_notify_enable; #define MAY_READ 4 #define MAY_APPEND 8 #define MAY_ACCESS 16 -#define MAY_CHDIR 32 -#define MAY_OPEN 64 +#define MAY_OPEN 32 #define FMODE_READ 1 #define FMODE_WRITE 2 diff --git a/include/linux/namei.h b/include/linux/namei.h index 768773d57857..60e35a02f6cb 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -53,7 +53,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; */ #define LOOKUP_OPEN (0x0100) #define LOOKUP_CREATE (0x0200) -#define LOOKUP_ACCESS (0x0400) extern int __user_walk(const char __user *, unsigned, struct nameidata *); extern int __user_walk_fd(int dfd, const char __user *, unsigned, struct nameidata *); -- cgit From b77b0646ef4efe31a7449bb3d9360fd00f95433d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 17 Jul 2008 09:37:02 -0400 Subject: [PATCH] pass MAY_OPEN to vfs_permission() explicitly ... and get rid of the last "let's deduce mask from nameidata->flags" bit. Signed-off-by: Al Viro --- fs/exec.c | 4 ++-- fs/namei.c | 13 ++++--------- include/linux/security.h | 7 +++---- security/capability.c | 3 +-- security/security.c | 4 ++-- security/selinux/hooks.c | 5 ++--- security/smack/smack_lsm.c | 3 +-- 7 files changed, 15 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index b8792a131533..0ba5d355c5a1 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -118,7 +118,7 @@ asmlinkage long sys_uselib(const char __user * library) if (!S_ISREG(nd.path.dentry->d_inode->i_mode)) goto exit; - error = vfs_permission(&nd, MAY_READ | MAY_EXEC); + error = vfs_permission(&nd, MAY_READ | MAY_EXEC | MAY_OPEN); if (error) goto exit; @@ -666,7 +666,7 @@ struct file *open_exec(const char *name) struct inode *inode = nd.path.dentry->d_inode; file = ERR_PTR(-EACCES); if (S_ISREG(inode->i_mode)) { - int err = vfs_permission(&nd, MAY_EXEC); + int err = vfs_permission(&nd, MAY_EXEC | MAY_OPEN); file = ERR_PTR(err); if (!err) { file = nameidata_to_filp(&nd, diff --git a/fs/namei.c b/fs/namei.c index 33dcaf025c49..6b0e8e5e079e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -263,12 +263,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) /* Ordinary permission routines do not understand MAY_APPEND. */ if (inode->i_op && inode->i_op->permission) { - int extra = 0; - if (nd) { - if (nd->flags & LOOKUP_OPEN) - extra |= MAY_OPEN; - } - retval = inode->i_op->permission(inode, mask | extra); + retval = inode->i_op->permission(inode, mask); if (!retval) { /* * Exec permission on a regular file is denied if none @@ -292,7 +287,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) return retval; return security_inode_permission(inode, - mask & (MAY_READ|MAY_WRITE|MAY_EXEC), nd); + mask & (MAY_READ|MAY_WRITE|MAY_EXEC)); } /** @@ -492,7 +487,7 @@ static int exec_permission_lite(struct inode *inode, return -EACCES; ok: - return security_inode_permission(inode, MAY_EXEC, nd); + return security_inode_permission(inode, MAY_EXEC); } /* @@ -1692,7 +1687,7 @@ struct file *do_filp_open(int dfd, const char *pathname, int will_write; int flag = open_to_namei_flags(open_flag); - acc_mode = ACC_MODE(flag); + acc_mode = MAY_OPEN | ACC_MODE(flag); /* O_TRUNC implies we need access checks for write permissions */ if (flag & O_TRUNC) diff --git a/include/linux/security.h b/include/linux/security.h index f0e9adb22ac2..fd96e7f8a6f9 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1362,7 +1362,7 @@ struct security_operations { struct inode *new_dir, struct dentry *new_dentry); int (*inode_readlink) (struct dentry *dentry); int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd); - int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd); + int (*inode_permission) (struct inode *inode, int mask); int (*inode_setattr) (struct dentry *dentry, struct iattr *attr); int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry); void (*inode_delete) (struct inode *inode); @@ -1628,7 +1628,7 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry); int security_inode_readlink(struct dentry *dentry); int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd); -int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd); +int security_inode_permission(struct inode *inode, int mask); int security_inode_setattr(struct dentry *dentry, struct iattr *attr); int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry); void security_inode_delete(struct inode *inode); @@ -2021,8 +2021,7 @@ static inline int security_inode_follow_link(struct dentry *dentry, return 0; } -static inline int security_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static inline int security_inode_permission(struct inode *inode, int mask) { return 0; } diff --git a/security/capability.c b/security/capability.c index 5b01c0b02422..63d10da515a5 100644 --- a/security/capability.c +++ b/security/capability.c @@ -211,8 +211,7 @@ static int cap_inode_follow_link(struct dentry *dentry, return 0; } -static int cap_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int cap_inode_permission(struct inode *inode, int mask) { return 0; } diff --git a/security/security.c b/security/security.c index 59f23b5918b3..78ed3ffde242 100644 --- a/security/security.c +++ b/security/security.c @@ -429,11 +429,11 @@ int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd) return security_ops->inode_follow_link(dentry, nd); } -int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd) +int security_inode_permission(struct inode *inode, int mask) { if (unlikely(IS_PRIVATE(inode))) return 0; - return security_ops->inode_permission(inode, mask, nd); + return security_ops->inode_permission(inode, mask); } int security_inode_setattr(struct dentry *dentry, struct iattr *attr) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 3481cde5bf15..5ba13908b5b4 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2624,12 +2624,11 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na return dentry_has_perm(current, NULL, dentry, FILE__READ); } -static int selinux_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int selinux_inode_permission(struct inode *inode, int mask) { int rc; - rc = secondary_ops->inode_permission(inode, mask, nd); + rc = secondary_ops->inode_permission(inode, mask); if (rc) return rc; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index ee5a51cbc5eb..1b40e558f983 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -522,8 +522,7 @@ static int smack_inode_rename(struct inode *old_inode, * * Returns 0 if access is permitted, -EACCES otherwise */ -static int smack_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +static int smack_inode_permission(struct inode *inode, int mask) { /* * No permission to check. Existence test. Yup, it's there. -- cgit From 88b387824fdaecb6ba0f471acf0aadf7d24739fd Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 21 Jul 2008 18:06:36 +0800 Subject: [PATCH] vfs: use kstrdup() and check failing allocation - use kstrdup() instead of kmalloc() + memcpy() - return NULL if allocating ->mnt_devname failed - mnt_devname should be const Signed-off-by: Li Zefan Acked-by: Cyrill Gorcunov Signed-off-by: Al Viro --- fs/namespace.c | 24 +++++++++++++----------- include/linux/mount.h | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/fs/namespace.c b/fs/namespace.c index c4fcf48acef8..26380f599534 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -112,9 +112,13 @@ struct vfsmount *alloc_vfsmnt(const char *name) int err; err = mnt_alloc_id(mnt); - if (err) { - kmem_cache_free(mnt_cache, mnt); - return NULL; + if (err) + goto out_free_cache; + + if (name) { + mnt->mnt_devname = kstrdup(name, GFP_KERNEL); + if (!mnt->mnt_devname) + goto out_free_id; } atomic_set(&mnt->mnt_count, 1); @@ -127,16 +131,14 @@ struct vfsmount *alloc_vfsmnt(const char *name) INIT_LIST_HEAD(&mnt->mnt_slave_list); INIT_LIST_HEAD(&mnt->mnt_slave); atomic_set(&mnt->__mnt_writers, 0); - if (name) { - int size = strlen(name) + 1; - char *newname = kmalloc(size, GFP_KERNEL); - if (newname) { - memcpy(newname, name, size); - mnt->mnt_devname = newname; - } - } } return mnt; + +out_free_id: + mnt_free_id(mnt); +out_free_cache: + kmem_cache_free(mnt_cache, mnt); + return NULL; } /* diff --git a/include/linux/mount.h b/include/linux/mount.h index 4374d1adeb4b..b5efaa2132ab 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -47,7 +47,7 @@ struct vfsmount { struct list_head mnt_child; /* and going through their mnt_child */ int mnt_flags; /* 4 bytes hole on 64bits arches */ - char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ + const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; struct list_head mnt_expire; /* link in fs-specific expiry list */ struct list_head mnt_share; /* circular list of shared mounts */ -- cgit From 9767d74957450da6365c363d69e3d02d605d7375 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 1 Jul 2008 15:01:26 +0200 Subject: [patch 1/4] vfs: utimes: move owner check into inode_change_ok() Add a new ia_valid flag: ATTR_TIMES_SET, to handle the UTIMES_OMIT/UTIMES_NOW and UTIMES_NOW/UTIMES_OMIT cases. In these cases neither ATTR_MTIME_SET nor ATTR_ATIME_SET is in the flags, yet the POSIX draft specifies that permission checking is performed the same way as if one or both of the times was explicitly set to a timestamp. See the path "vfs: utimensat(): fix error checking for {UTIME_NOW,UTIME_OMIT} case" by Michael Kerrisk for the patch introducing this behavior. This is a cleanup, as well as allowing filesystems (NFS/fuse/...) to perform their own permission checking instead of the default. CC: Ulrich Drepper CC: Michael Kerrisk Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/attr.c | 2 +- fs/utimes.c | 17 ++++------------- include/linux/fs.h | 33 +++++++++++++++++---------------- 3 files changed, 22 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/fs/attr.c b/fs/attr.c index 966b73e25f82..765fc75fab3b 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -51,7 +51,7 @@ int inode_change_ok(struct inode *inode, struct iattr *attr) } /* Check for setting the inode time. */ - if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) { + if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) { if (!is_owner_or_cap(inode)) goto error; } diff --git a/fs/utimes.c b/fs/utimes.c index b6b664e7145e..ecf8941ba34a 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -101,7 +101,6 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags times[1].tv_nsec == UTIME_NOW) times = NULL; - /* In most cases, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = -EPERM; @@ -123,21 +122,13 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; newattrs.ia_valid |= ATTR_MTIME_SET; } - /* - * For the UTIME_OMIT/UTIME_NOW and UTIME_NOW/UTIME_OMIT - * cases, we need to make an extra check that is not done by - * inode_change_ok(). + * Tell inode_change_ok(), that this is an explicit time + * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET + * were used. */ - if (((times[0].tv_nsec == UTIME_NOW && - times[1].tv_nsec == UTIME_OMIT) - || - (times[0].tv_nsec == UTIME_OMIT && - times[1].tv_nsec == UTIME_NOW)) - && !is_owner_or_cap(inode)) - goto mnt_drop_write_and_out; + newattrs.ia_valid |= ATTR_TIMES_SET; } else { - /* * If times is NULL (or both times are UTIME_NOW), * then we need to check permissions, because diff --git a/include/linux/fs.h b/include/linux/fs.h index d8721e818b45..527b9e482f99 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -320,22 +320,23 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, * Attribute flags. These should be or-ed together to figure out what * has been changed! */ -#define ATTR_MODE 1 -#define ATTR_UID 2 -#define ATTR_GID 4 -#define ATTR_SIZE 8 -#define ATTR_ATIME 16 -#define ATTR_MTIME 32 -#define ATTR_CTIME 64 -#define ATTR_ATIME_SET 128 -#define ATTR_MTIME_SET 256 -#define ATTR_FORCE 512 /* Not a change, but a change it */ -#define ATTR_ATTR_FLAG 1024 -#define ATTR_KILL_SUID 2048 -#define ATTR_KILL_SGID 4096 -#define ATTR_FILE 8192 -#define ATTR_KILL_PRIV 16384 -#define ATTR_OPEN 32768 /* Truncating from open(O_TRUNC) */ +#define ATTR_MODE (1 << 0) +#define ATTR_UID (1 << 1) +#define ATTR_GID (1 << 2) +#define ATTR_SIZE (1 << 3) +#define ATTR_ATIME (1 << 4) +#define ATTR_MTIME (1 << 5) +#define ATTR_CTIME (1 << 6) +#define ATTR_ATIME_SET (1 << 7) +#define ATTR_MTIME_SET (1 << 8) +#define ATTR_FORCE (1 << 9) /* Not a change, but a change it */ +#define ATTR_ATTR_FLAG (1 << 10) +#define ATTR_KILL_SUID (1 << 11) +#define ATTR_KILL_SGID (1 << 12) +#define ATTR_FILE (1 << 13) +#define ATTR_KILL_PRIV (1 << 14) +#define ATTR_OPEN (1 << 15) /* Truncating from open(O_TRUNC) */ +#define ATTR_TIMES_SET (1 << 16) /* * This is the Inode Attributes structure, used for notify_change(). It -- cgit From f419a2e3b64def707e1384ee38abb77f99af5f6d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 22 Jul 2008 00:07:17 -0400 Subject: [PATCH] kill nameidata passing to permission(), rename to inode_permission() Incidentally, the name that gives hundreds of false positives on grep is not a good idea... Signed-off-by: Al Viro --- fs/ecryptfs/inode.c | 2 +- fs/namei.c | 22 +++++++++------------- fs/nfsd/nfsfh.c | 2 +- fs/nfsd/vfs.c | 4 ++-- fs/utimes.c | 2 +- fs/xattr.c | 2 +- include/linux/fs.h | 2 +- ipc/mqueue.c | 2 +- 8 files changed, 17 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index f25caf2b0887..89209f00f9c7 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -830,7 +830,7 @@ out: static int ecryptfs_permission(struct inode *inode, int mask) { - return permission(ecryptfs_inode_to_lower(inode), mask, NULL); + return inode_permission(ecryptfs_inode_to_lower(inode), mask); } /** diff --git a/fs/namei.c b/fs/namei.c index 396cb3e5c364..5029b93ebbd5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -227,13 +227,9 @@ int generic_permission(struct inode *inode, int mask, return -EACCES; } -int permission(struct inode *inode, int mask, struct nameidata *nd) +int inode_permission(struct inode *inode, int mask) { int retval; - struct vfsmount *mnt = NULL; - - if (nd) - mnt = nd->path.mnt; if (mask & MAY_WRITE) { umode_t mode = inode->i_mode; @@ -293,7 +289,7 @@ int permission(struct inode *inode, int mask, struct nameidata *nd) */ int vfs_permission(struct nameidata *nd, int mask) { - return permission(nd->path.dentry->d_inode, mask, nd); + return inode_permission(nd->path.dentry->d_inode, mask); } /** @@ -310,7 +306,7 @@ int vfs_permission(struct nameidata *nd, int mask) */ int file_permission(struct file *file, int mask) { - return permission(file->f_path.dentry->d_inode, mask, NULL); + return inode_permission(file->f_path.dentry->d_inode, mask); } /* @@ -1262,7 +1258,7 @@ static struct dentry *lookup_hash(struct nameidata *nd) { int err; - err = permission(nd->path.dentry->d_inode, MAY_EXEC, nd); + err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC); if (err) return ERR_PTR(err); return __lookup_hash(&nd->last, nd->path.dentry, nd); @@ -1310,7 +1306,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) if (err) return ERR_PTR(err); - err = permission(base->d_inode, MAY_EXEC, NULL); + err = inode_permission(base->d_inode, MAY_EXEC); if (err) return ERR_PTR(err); return __lookup_hash(&this, base, NULL); @@ -1400,7 +1396,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) BUG_ON(victim->d_parent->d_inode != dir); audit_inode_child(victim->d_name.name, victim, dir); - error = permission(dir,MAY_WRITE | MAY_EXEC, NULL); + error = inode_permission(dir, MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) @@ -1437,7 +1433,7 @@ static inline int may_create(struct inode *dir, struct dentry *child, return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; - return permission(dir,MAY_WRITE | MAY_EXEC, nd); + return inode_permission(dir, MAY_WRITE | MAY_EXEC); } /* @@ -2543,7 +2539,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, * we'll need to flip '..'. */ if (new_dir != old_dir) { - error = permission(old_dentry->d_inode, MAY_WRITE, NULL); + error = inode_permission(old_dentry->d_inode, MAY_WRITE); if (error) return error; } @@ -2897,7 +2893,7 @@ EXPORT_SYMBOL(page_symlink); EXPORT_SYMBOL(page_symlink_inode_operations); EXPORT_SYMBOL(path_lookup); EXPORT_SYMBOL(vfs_path_lookup); -EXPORT_SYMBOL(permission); +EXPORT_SYMBOL(inode_permission); EXPORT_SYMBOL(vfs_permission); EXPORT_SYMBOL(file_permission); EXPORT_SYMBOL(unlock_rename); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index f45451eb1e38..ea37c96f0445 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -51,7 +51,7 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry) /* make sure parents give x permission to user */ int err; parent = dget_parent(tdentry); - err = permission(parent->d_inode, MAY_EXEC, NULL); + err = inode_permission(parent->d_inode, MAY_EXEC); if (err < 0) { dput(parent); break; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index ad1ad59e3742..18060bed5267 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1953,12 +1953,12 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, return 0; /* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */ - err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), NULL); + err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC)); /* Allow read access to binaries even when mode 111 */ if (err == -EACCES && S_ISREG(inode->i_mode) && acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE)) - err = permission(inode, MAY_EXEC, NULL); + err = inode_permission(inode, MAY_EXEC); return err? nfserrno(err) : 0; } diff --git a/fs/utimes.c b/fs/utimes.c index dad679d3a158..dc28b7826259 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -96,7 +96,7 @@ static int utimes_common(struct path *path, struct timespec *times) goto mnt_drop_write_and_out; if (!is_owner_or_cap(inode)) { - error = permission(inode, MAY_WRITE, NULL); + error = inode_permission(inode, MAY_WRITE); if (error) goto mnt_drop_write_and_out; } diff --git a/fs/xattr.c b/fs/xattr.c index 4706a8b1f495..b96222e05ba0 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -63,7 +63,7 @@ xattr_permission(struct inode *inode, const char *name, int mask) return -EPERM; } - return permission(inode, mask, NULL); + return inode_permission(inode, mask); } int diff --git a/include/linux/fs.h b/include/linux/fs.h index 527b9e482f99..9d2de4cadabd 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1770,7 +1770,7 @@ extern int do_remount_sb(struct super_block *sb, int flags, extern sector_t bmap(struct inode *, sector_t); #endif extern int notify_change(struct dentry *, struct iattr *); -extern int permission(struct inode *, int, struct nameidata *); +extern int inode_permission(struct inode *, int); extern int generic_permission(struct inode *, int, int (*check_acl)(struct inode *, int)); diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 474984f9e032..96fb36cd9874 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -638,7 +638,7 @@ static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, return ERR_PTR(-EINVAL); } - if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL)) { + if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) { dput(dentry); mntput(mqueue_mnt); return ERR_PTR(-EACCES); -- cgit From 2d8f30380ab8c706f4e0a8f1aaa22b5886e9ac8a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 22 Jul 2008 09:59:21 -0400 Subject: [PATCH] sanitize __user_walk_fd() et.al. * do not pass nameidata; struct path is all the callers want. * switch to new helpers: user_path_at(dfd, pathname, flags, &path) user_path(pathname, &path) user_lpath(pathname, &path) user_path_dir(pathname, &path) (fail if not a directory) The last 3 are trivial macro wrappers for the first one. * remove nameidata in callers. Signed-off-by: Al Viro --- arch/alpha/kernel/osf_sys.c | 10 ++-- arch/parisc/hpux/sys_hpux.c | 10 ++-- fs/coda/pioctl.c | 14 ++--- fs/compat.c | 20 +++---- fs/inotify_user.c | 22 ++++---- fs/namei.c | 36 ++++++------- fs/namespace.c | 74 +++++++++++++------------- fs/open.c | 124 +++++++++++++++++++++---------------------- fs/stat.c | 32 +++++------ fs/utimes.c | 8 +-- fs/xattr.c | 96 ++++++++++++++++----------------- fs/xfs/linux-2.6/xfs_ioctl.c | 14 +++-- include/linux/namei.h | 13 ++--- 13 files changed, 235 insertions(+), 238 deletions(-) (limited to 'include/linux') diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 32ca1b927307..6e943135f0e0 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -253,15 +253,15 @@ do_osf_statfs(struct dentry * dentry, struct osf_statfs __user *buffer, } asmlinkage int -osf_statfs(char __user *path, struct osf_statfs __user *buffer, unsigned long bufsiz) +osf_statfs(char __user *pathname, struct osf_statfs __user *buffer, unsigned long bufsiz) { - struct nameidata nd; + struct path path; int retval; - retval = user_path_walk(path, &nd); + retval = user_path(pathname, &path); if (!retval) { - retval = do_osf_statfs(nd.path.dentry, buffer, bufsiz); - path_put(&nd.path); + retval = do_osf_statfs(path.dentry, buffer, bufsiz); + path_put(&path); } return retval; } diff --git a/arch/parisc/hpux/sys_hpux.c b/arch/parisc/hpux/sys_hpux.c index be255ebb609c..18072e03a019 100644 --- a/arch/parisc/hpux/sys_hpux.c +++ b/arch/parisc/hpux/sys_hpux.c @@ -210,19 +210,19 @@ static int vfs_statfs_hpux(struct dentry *dentry, struct hpux_statfs *buf) } /* hpux statfs */ -asmlinkage long hpux_statfs(const char __user *path, +asmlinkage long hpux_statfs(const char __user *pathname, struct hpux_statfs __user *buf) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct hpux_statfs tmp; - error = vfs_statfs_hpux(nd.path.dentry, &tmp); + error = vfs_statfs_hpux(path.dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; - path_put(&nd.path); + path_put(&path); } return error; } diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index c38a98974fb0..c51365422aa8 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -49,7 +49,7 @@ static int coda_ioctl_permission(struct inode *inode, int mask) static int coda_pioctl(struct inode * inode, struct file * filp, unsigned int cmd, unsigned long user_data) { - struct nameidata nd; + struct path path; int error; struct PioctlData data; struct inode *target_inode = NULL; @@ -64,21 +64,21 @@ static int coda_pioctl(struct inode * inode, struct file * filp, * Look up the pathname. Note that the pathname is in * user memory, and namei takes care of this */ - if ( data.follow ) { - error = user_path_walk(data.path, &nd); + if (data.follow) { + error = user_path(data.path, &path); } else { - error = user_path_walk_link(data.path, &nd); + error = user_lpath(data.path, &path); } if ( error ) { return error; } else { - target_inode = nd.path.dentry->d_inode; + target_inode = path.dentry->d_inode; } /* return if it is not a Coda inode */ if ( target_inode->i_sb != inode->i_sb ) { - path_put(&nd.path); + path_put(&path); return -EINVAL; } @@ -87,7 +87,7 @@ static int coda_pioctl(struct inode * inode, struct file * filp, error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data); - path_put(&nd.path); + path_put(&path); return error; } diff --git a/fs/compat.c b/fs/compat.c index 106eba28ec5a..c9d1472e65c5 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -234,18 +234,18 @@ static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs * * The following statfs calls are copies of code from fs/open.c and * should be checked against those from time to time */ -asmlinkage long compat_sys_statfs(const char __user *path, struct compat_statfs __user *buf) +asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct kstatfs tmp; - error = vfs_statfs(nd.path.dentry, &tmp); + error = vfs_statfs(path.dentry, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); - path_put(&nd.path); + path_put(&path); } return error; } @@ -299,21 +299,21 @@ static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstat return 0; } -asmlinkage long compat_sys_statfs64(const char __user *path, compat_size_t sz, struct compat_statfs64 __user *buf) +asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf) { - struct nameidata nd; + struct path path; int error; if (sz != sizeof(*buf)) return -EINVAL; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct kstatfs tmp; - error = vfs_statfs(nd.path.dentry, &tmp); + error = vfs_statfs(path.dentry, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); - path_put(&nd.path); + path_put(&path); } return error; } diff --git a/fs/inotify_user.c b/fs/inotify_user.c index 9b99ebf28884..60249429a253 100644 --- a/fs/inotify_user.c +++ b/fs/inotify_user.c @@ -354,20 +354,20 @@ static void inotify_dev_event_dequeue(struct inotify_device *dev) } /* - * find_inode - resolve a user-given path to a specific inode and return a nd + * find_inode - resolve a user-given path to a specific inode */ -static int find_inode(const char __user *dirname, struct nameidata *nd, +static int find_inode(const char __user *dirname, struct path *path, unsigned flags) { int error; - error = __user_walk(dirname, flags, nd); + error = user_path_at(AT_FDCWD, dirname, flags, path); if (error) return error; /* you can only watch an inode if you have read permissions on it */ - error = inode_permission(nd->path.dentry->d_inode, MAY_READ); + error = inode_permission(path->dentry->d_inode, MAY_READ); if (error) - path_put(&nd->path); + path_put(path); return error; } @@ -650,11 +650,11 @@ asmlinkage long sys_inotify_init(void) return sys_inotify_init1(0); } -asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) +asmlinkage long sys_inotify_add_watch(int fd, const char __user *pathname, u32 mask) { struct inode *inode; struct inotify_device *dev; - struct nameidata nd; + struct path path; struct file *filp; int ret, fput_needed; unsigned flags = 0; @@ -674,12 +674,12 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) if (mask & IN_ONLYDIR) flags |= LOOKUP_DIRECTORY; - ret = find_inode(path, &nd, flags); + ret = find_inode(pathname, &path, flags); if (unlikely(ret)) goto fput_and_out; - /* inode held in place by reference to nd; dev by fget on fd */ - inode = nd.path.dentry->d_inode; + /* inode held in place by reference to path; dev by fget on fd */ + inode = path.dentry->d_inode; dev = filp->private_data; mutex_lock(&dev->up_mutex); @@ -688,7 +688,7 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) ret = create_watch(dev, inode, mask); mutex_unlock(&dev->up_mutex); - path_put(&nd.path); + path_put(&path); fput_and_out: fput_light(filp, fput_needed); return ret; diff --git a/fs/namei.c b/fs/namei.c index 5029b93ebbd5..edb5e973f9b3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1334,24 +1334,24 @@ struct dentry *lookup_one_noperm(const char *name, struct dentry *base) return __lookup_hash(&this, base, NULL); } -int __user_walk_fd(int dfd, const char __user *name, unsigned flags, - struct nameidata *nd) +int user_path_at(int dfd, const char __user *name, unsigned flags, + struct path *path) { + struct nameidata nd; char *tmp = getname(name); int err = PTR_ERR(tmp); - if (!IS_ERR(tmp)) { - err = do_path_lookup(dfd, tmp, flags, nd); + + BUG_ON(flags & LOOKUP_PARENT); + + err = do_path_lookup(dfd, tmp, flags, &nd); putname(tmp); + if (!err) + *path = nd.path; } return err; } -int __user_walk(const char __user *name, unsigned flags, struct nameidata *nd) -{ - return __user_walk_fd(AT_FDCWD, name, flags, nd); -} - /* * It's inline, so penalty for filesystems that don't use sticky bit is * minimal. @@ -2446,7 +2446,8 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname, int flags) { struct dentry *new_dentry; - struct nameidata nd, old_nd; + struct nameidata nd; + struct path old_path; int error; char * to; @@ -2457,16 +2458,16 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname, if (IS_ERR(to)) return PTR_ERR(to); - error = __user_walk_fd(olddfd, oldname, - flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0, - &old_nd); + error = user_path_at(olddfd, oldname, + flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0, + &old_path); if (error) goto exit; error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd); if (error) goto out; error = -EXDEV; - if (old_nd.path.mnt != nd.path.mnt) + if (old_path.mnt != nd.path.mnt) goto out_release; new_dentry = lookup_create(&nd, 0); error = PTR_ERR(new_dentry); @@ -2475,7 +2476,7 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname, error = mnt_want_write(nd.path.mnt); if (error) goto out_dput; - error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry); + error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry); mnt_drop_write(nd.path.mnt); out_dput: dput(new_dentry); @@ -2484,7 +2485,7 @@ out_unlock: out_release: path_put(&nd.path); out: - path_put(&old_nd.path); + path_put(&old_path); exit: putname(to); @@ -2877,8 +2878,7 @@ const struct inode_operations page_symlink_inode_operations = { .put_link = page_put_link, }; -EXPORT_SYMBOL(__user_walk); -EXPORT_SYMBOL(__user_walk_fd); +EXPORT_SYMBOL(user_path_at); EXPORT_SYMBOL(follow_down); EXPORT_SYMBOL(follow_up); EXPORT_SYMBOL(get_write_access); /* binfmt_aout */ diff --git a/fs/namespace.c b/fs/namespace.c index 26380f599534..411728c0c8bb 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1130,27 +1130,27 @@ static int do_umount(struct vfsmount *mnt, int flags) asmlinkage long sys_umount(char __user * name, int flags) { - struct nameidata nd; + struct path path; int retval; - retval = __user_walk(name, LOOKUP_FOLLOW, &nd); + retval = user_path(name, &path); if (retval) goto out; retval = -EINVAL; - if (nd.path.dentry != nd.path.mnt->mnt_root) + if (path.dentry != path.mnt->mnt_root) goto dput_and_out; - if (!check_mnt(nd.path.mnt)) + if (!check_mnt(path.mnt)) goto dput_and_out; retval = -EPERM; if (!capable(CAP_SYS_ADMIN)) goto dput_and_out; - retval = do_umount(nd.path.mnt, flags); + retval = do_umount(path.mnt, flags); dput_and_out: /* we mustn't call path_put() as that would clear mnt_expiry_mark */ - dput(nd.path.dentry); - mntput_no_expire(nd.path.mnt); + dput(path.dentry); + mntput_no_expire(path.mnt); out: return retval; } @@ -2179,28 +2179,26 @@ asmlinkage long sys_pivot_root(const char __user * new_root, const char __user * put_old) { struct vfsmount *tmp; - struct nameidata new_nd, old_nd; - struct path parent_path, root_parent, root; + struct path new, old, parent_path, root_parent, root; int error; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, - &new_nd); + error = user_path_dir(new_root, &new); if (error) goto out0; error = -EINVAL; - if (!check_mnt(new_nd.path.mnt)) + if (!check_mnt(new.mnt)) goto out1; - error = __user_walk(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_nd); + error = user_path_dir(put_old, &old); if (error) goto out1; - error = security_sb_pivotroot(&old_nd.path, &new_nd.path); + error = security_sb_pivotroot(&old, &new); if (error) { - path_put(&old_nd.path); + path_put(&old); goto out1; } @@ -2209,69 +2207,69 @@ asmlinkage long sys_pivot_root(const char __user * new_root, path_get(¤t->fs->root); read_unlock(¤t->fs->lock); down_write(&namespace_sem); - mutex_lock(&old_nd.path.dentry->d_inode->i_mutex); + mutex_lock(&old.dentry->d_inode->i_mutex); error = -EINVAL; - if (IS_MNT_SHARED(old_nd.path.mnt) || - IS_MNT_SHARED(new_nd.path.mnt->mnt_parent) || + if (IS_MNT_SHARED(old.mnt) || + IS_MNT_SHARED(new.mnt->mnt_parent) || IS_MNT_SHARED(root.mnt->mnt_parent)) goto out2; if (!check_mnt(root.mnt)) goto out2; error = -ENOENT; - if (IS_DEADDIR(new_nd.path.dentry->d_inode)) + if (IS_DEADDIR(new.dentry->d_inode)) goto out2; - if (d_unhashed(new_nd.path.dentry) && !IS_ROOT(new_nd.path.dentry)) + if (d_unhashed(new.dentry) && !IS_ROOT(new.dentry)) goto out2; - if (d_unhashed(old_nd.path.dentry) && !IS_ROOT(old_nd.path.dentry)) + if (d_unhashed(old.dentry) && !IS_ROOT(old.dentry)) goto out2; error = -EBUSY; - if (new_nd.path.mnt == root.mnt || - old_nd.path.mnt == root.mnt) + if (new.mnt == root.mnt || + old.mnt == root.mnt) goto out2; /* loop, on the same file system */ error = -EINVAL; if (root.mnt->mnt_root != root.dentry) goto out2; /* not a mountpoint */ if (root.mnt->mnt_parent == root.mnt) goto out2; /* not attached */ - if (new_nd.path.mnt->mnt_root != new_nd.path.dentry) + if (new.mnt->mnt_root != new.dentry) goto out2; /* not a mountpoint */ - if (new_nd.path.mnt->mnt_parent == new_nd.path.mnt) + if (new.mnt->mnt_parent == new.mnt) goto out2; /* not attached */ /* make sure we can reach put_old from new_root */ - tmp = old_nd.path.mnt; + tmp = old.mnt; spin_lock(&vfsmount_lock); - if (tmp != new_nd.path.mnt) { + if (tmp != new.mnt) { for (;;) { if (tmp->mnt_parent == tmp) goto out3; /* already mounted on put_old */ - if (tmp->mnt_parent == new_nd.path.mnt) + if (tmp->mnt_parent == new.mnt) break; tmp = tmp->mnt_parent; } - if (!is_subdir(tmp->mnt_mountpoint, new_nd.path.dentry)) + if (!is_subdir(tmp->mnt_mountpoint, new.dentry)) goto out3; - } else if (!is_subdir(old_nd.path.dentry, new_nd.path.dentry)) + } else if (!is_subdir(old.dentry, new.dentry)) goto out3; - detach_mnt(new_nd.path.mnt, &parent_path); + detach_mnt(new.mnt, &parent_path); detach_mnt(root.mnt, &root_parent); /* mount old root on put_old */ - attach_mnt(root.mnt, &old_nd.path); + attach_mnt(root.mnt, &old); /* mount new_root on / */ - attach_mnt(new_nd.path.mnt, &root_parent); + attach_mnt(new.mnt, &root_parent); touch_mnt_namespace(current->nsproxy->mnt_ns); spin_unlock(&vfsmount_lock); - chroot_fs_refs(&root, &new_nd.path); - security_sb_post_pivotroot(&root, &new_nd.path); + chroot_fs_refs(&root, &new); + security_sb_post_pivotroot(&root, &new); error = 0; path_put(&root_parent); path_put(&parent_path); out2: - mutex_unlock(&old_nd.path.dentry->d_inode->i_mutex); + mutex_unlock(&old.dentry->d_inode->i_mutex); up_write(&namespace_sem); path_put(&root); - path_put(&old_nd.path); + path_put(&old); out1: - path_put(&new_nd.path); + path_put(&new); out0: return error; out3: diff --git a/fs/open.c b/fs/open.c index e94266700eda..3fe1a6857c75 100644 --- a/fs/open.c +++ b/fs/open.c @@ -122,37 +122,37 @@ static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf) return 0; } -asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf) +asmlinkage long sys_statfs(const char __user *pathname, struct statfs __user * buf) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct statfs tmp; - error = vfs_statfs_native(nd.path.dentry, &tmp); + error = vfs_statfs_native(path.dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; - path_put(&nd.path); + path_put(&path); } return error; } -asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64 __user *buf) +asmlinkage long sys_statfs64(const char __user *pathname, size_t sz, struct statfs64 __user *buf) { - struct nameidata nd; + struct path path; long error; if (sz != sizeof(*buf)) return -EINVAL; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (!error) { struct statfs64 tmp; - error = vfs_statfs64(nd.path.dentry, &tmp); + error = vfs_statfs64(path.dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; - path_put(&nd.path); + path_put(&path); } return error; } @@ -223,20 +223,20 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, return err; } -static long do_sys_truncate(const char __user * path, loff_t length) +static long do_sys_truncate(const char __user *pathname, loff_t length) { - struct nameidata nd; - struct inode * inode; + struct path path; + struct inode *inode; int error; error = -EINVAL; if (length < 0) /* sorry, but loff_t says... */ goto out; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) goto out; - inode = nd.path.dentry->d_inode; + inode = path.dentry->d_inode; /* For directories it's -EISDIR, for other non-regulars - -EINVAL */ error = -EISDIR; @@ -247,7 +247,7 @@ static long do_sys_truncate(const char __user * path, loff_t length) if (!S_ISREG(inode->i_mode)) goto dput_and_out; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto dput_and_out; @@ -274,15 +274,15 @@ static long do_sys_truncate(const char __user * path, loff_t length) error = locks_verify_truncate(inode, NULL, length); if (!error) { DQUOT_INIT(inode); - error = do_truncate(nd.path.dentry, length, 0, NULL); + error = do_truncate(path.dentry, length, 0, NULL); } put_write_and_out: put_write_access(inode); mnt_drop_write_and_out: - mnt_drop_write(nd.path.mnt); + mnt_drop_write(path.mnt); dput_and_out: - path_put(&nd.path); + path_put(&path); out: return error; } @@ -425,7 +425,7 @@ out: */ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) { - struct nameidata nd; + struct path path; struct inode *inode; int old_fsuid, old_fsgid; kernel_cap_t uninitialized_var(old_cap); /* !SECURE_NO_SETUID_FIXUP */ @@ -449,7 +449,7 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) * FIXME: There is a race here against sys_capset. The * capabilities can change yet we will restore the old * value below. We should hold task_capabilities_lock, - * but we cannot because user_path_walk can sleep. + * but we cannot because user_path_at can sleep. */ #endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */ if (current->uid) @@ -458,11 +458,11 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) old_cap = cap_set_effective(current->cap_permitted); } - res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); + res = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path); if (res) goto out; - inode = nd.path.dentry->d_inode; + inode = path.dentry->d_inode; if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) { /* @@ -470,7 +470,7 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) * with the "noexec" flag. */ res = -EACCES; - if (nd.path.mnt->mnt_flags & MNT_NOEXEC) + if (path.mnt->mnt_flags & MNT_NOEXEC) goto out_path_release; } @@ -488,11 +488,11 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode) * inherently racy and know that the fs may change * state before we even see this result. */ - if (__mnt_is_readonly(nd.path.mnt)) + if (__mnt_is_readonly(path.mnt)) res = -EROFS; out_path_release: - path_put(&nd.path); + path_put(&path); out: current->fsuid = old_fsuid; current->fsgid = old_fsgid; @@ -510,21 +510,21 @@ asmlinkage long sys_access(const char __user *filename, int mode) asmlinkage long sys_chdir(const char __user * filename) { - struct nameidata nd; + struct path path; int error; - error = __user_walk(filename, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &nd); + error = user_path_dir(filename, &path); if (error) goto out; - error = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); if (error) goto dput_and_out; - set_fs_pwd(current->fs, &nd.path); + set_fs_pwd(current->fs, &path); dput_and_out: - path_put(&nd.path); + path_put(&path); out: return error; } @@ -557,14 +557,14 @@ out: asmlinkage long sys_chroot(const char __user * filename) { - struct nameidata nd; + struct path path; int error; - error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd); + error = user_path_dir(filename, &path); if (error) goto out; - error = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); if (error) goto dput_and_out; @@ -572,10 +572,10 @@ asmlinkage long sys_chroot(const char __user * filename) if (!capable(CAP_SYS_CHROOT)) goto dput_and_out; - set_fs_root(current->fs, &nd.path); + set_fs_root(current->fs, &path); error = 0; dput_and_out: - path_put(&nd.path); + path_put(&path); out: return error; } @@ -617,17 +617,17 @@ out: asmlinkage long sys_fchmodat(int dfd, const char __user *filename, mode_t mode) { - struct nameidata nd; - struct inode * inode; + struct path path; + struct inode *inode; int error; struct iattr newattrs; - error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd); + error = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path); if (error) goto out; - inode = nd.path.dentry->d_inode; + inode = path.dentry->d_inode; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto dput_and_out; mutex_lock(&inode->i_mutex); @@ -635,11 +635,11 @@ asmlinkage long sys_fchmodat(int dfd, const char __user *filename, mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - error = notify_change(nd.path.dentry, &newattrs); + error = notify_change(path.dentry, &newattrs); mutex_unlock(&inode->i_mutex); - mnt_drop_write(nd.path.mnt); + mnt_drop_write(path.mnt); dput_and_out: - path_put(&nd.path); + path_put(&path); out: return error; } @@ -676,19 +676,19 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group) asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(filename, &nd); + error = user_path(filename, &path); if (error) goto out; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto out_release; - error = chown_common(nd.path.dentry, user, group); - mnt_drop_write(nd.path.mnt); + error = chown_common(path.dentry, user, group); + mnt_drop_write(path.mnt); out_release: - path_put(&nd.path); + path_put(&path); out: return error; } @@ -696,7 +696,7 @@ out: asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag) { - struct nameidata nd; + struct path path; int error = -EINVAL; int follow; @@ -704,35 +704,35 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, goto out; follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW; - error = __user_walk_fd(dfd, filename, follow, &nd); + error = user_path_at(dfd, filename, follow, &path); if (error) goto out; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto out_release; - error = chown_common(nd.path.dentry, user, group); - mnt_drop_write(nd.path.mnt); + error = chown_common(path.dentry, user, group); + mnt_drop_write(path.mnt); out_release: - path_put(&nd.path); + path_put(&path); out: return error; } asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk_link(filename, &nd); + error = user_lpath(filename, &path); if (error) goto out; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto out_release; - error = chown_common(nd.path.dentry, user, group); - mnt_drop_write(nd.path.mnt); + error = chown_common(path.dentry, user, group); + mnt_drop_write(path.mnt); out_release: - path_put(&nd.path); + path_put(&path); out: return error; } diff --git a/fs/stat.c b/fs/stat.c index 9cf41f719d50..7c46fbeb8b76 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -57,13 +57,13 @@ EXPORT_SYMBOL(vfs_getattr); int vfs_stat_fd(int dfd, char __user *name, struct kstat *stat) { - struct nameidata nd; + struct path path; int error; - error = __user_walk_fd(dfd, name, LOOKUP_FOLLOW, &nd); + error = user_path_at(dfd, name, LOOKUP_FOLLOW, &path); if (!error) { - error = vfs_getattr(nd.path.mnt, nd.path.dentry, stat); - path_put(&nd.path); + error = vfs_getattr(path.mnt, path.dentry, stat); + path_put(&path); } return error; } @@ -77,13 +77,13 @@ EXPORT_SYMBOL(vfs_stat); int vfs_lstat_fd(int dfd, char __user *name, struct kstat *stat) { - struct nameidata nd; + struct path path; int error; - error = __user_walk_fd(dfd, name, 0, &nd); + error = user_path_at(dfd, name, 0, &path); if (!error) { - error = vfs_getattr(nd.path.mnt, nd.path.dentry, stat); - path_put(&nd.path); + error = vfs_getattr(path.mnt, path.dentry, stat); + path_put(&path); } return error; } @@ -291,29 +291,29 @@ asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf) return error; } -asmlinkage long sys_readlinkat(int dfd, const char __user *path, +asmlinkage long sys_readlinkat(int dfd, const char __user *pathname, char __user *buf, int bufsiz) { - struct nameidata nd; + struct path path; int error; if (bufsiz <= 0) return -EINVAL; - error = __user_walk_fd(dfd, path, 0, &nd); + error = user_path_at(dfd, pathname, 0, &path); if (!error) { - struct inode *inode = nd.path.dentry->d_inode; + struct inode *inode = path.dentry->d_inode; error = -EINVAL; if (inode->i_op && inode->i_op->readlink) { - error = security_inode_readlink(nd.path.dentry); + error = security_inode_readlink(path.dentry); if (!error) { - touch_atime(nd.path.mnt, nd.path.dentry); - error = inode->i_op->readlink(nd.path.dentry, + touch_atime(path.mnt, path.dentry); + error = inode->i_op->readlink(path.dentry, buf, bufsiz); } } - path_put(&nd.path); + path_put(&path); } return error; } diff --git a/fs/utimes.c b/fs/utimes.c index dc28b7826259..6929e3e91d05 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -152,18 +152,18 @@ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags error = utimes_common(&file->f_path, times); fput(file); } else { - struct nameidata nd; + struct path path; int lookup_flags = 0; if (!(flags & AT_SYMLINK_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; - error = __user_walk_fd(dfd, filename, lookup_flags, &nd); + error = user_path_at(dfd, filename, lookup_flags, &path); if (error) goto out; - error = utimes_common(&nd.path, times); - path_put(&nd.path); + error = utimes_common(&path, times); + path_put(&path); } out: diff --git a/fs/xattr.c b/fs/xattr.c index b96222e05ba0..468377e66531 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -252,40 +252,40 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value, } asmlinkage long -sys_setxattr(const char __user *path, const char __user *name, +sys_setxattr(const char __user *pathname, const char __user *name, const void __user *value, size_t size, int flags) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) return error; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (!error) { - error = setxattr(nd.path.dentry, name, value, size, flags); - mnt_drop_write(nd.path.mnt); + error = setxattr(path.dentry, name, value, size, flags); + mnt_drop_write(path.mnt); } - path_put(&nd.path); + path_put(&path); return error; } asmlinkage long -sys_lsetxattr(const char __user *path, const char __user *name, +sys_lsetxattr(const char __user *pathname, const char __user *name, const void __user *value, size_t size, int flags) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk_link(path, &nd); + error = user_lpath(pathname, &path); if (error) return error; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (!error) { - error = setxattr(nd.path.dentry, name, value, size, flags); - mnt_drop_write(nd.path.mnt); + error = setxattr(path.dentry, name, value, size, flags); + mnt_drop_write(path.mnt); } - path_put(&nd.path); + path_put(&path); return error; } @@ -350,32 +350,32 @@ getxattr(struct dentry *d, const char __user *name, void __user *value, } asmlinkage ssize_t -sys_getxattr(const char __user *path, const char __user *name, +sys_getxattr(const char __user *pathname, const char __user *name, void __user *value, size_t size) { - struct nameidata nd; + struct path path; ssize_t error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) return error; - error = getxattr(nd.path.dentry, name, value, size); - path_put(&nd.path); + error = getxattr(path.dentry, name, value, size); + path_put(&path); return error; } asmlinkage ssize_t -sys_lgetxattr(const char __user *path, const char __user *name, void __user *value, +sys_lgetxattr(const char __user *pathname, const char __user *name, void __user *value, size_t size) { - struct nameidata nd; + struct path path; ssize_t error; - error = user_path_walk_link(path, &nd); + error = user_lpath(pathname, &path); if (error) return error; - error = getxattr(nd.path.dentry, name, value, size); - path_put(&nd.path); + error = getxattr(path.dentry, name, value, size); + path_put(&path); return error; } @@ -425,30 +425,30 @@ listxattr(struct dentry *d, char __user *list, size_t size) } asmlinkage ssize_t -sys_listxattr(const char __user *path, char __user *list, size_t size) +sys_listxattr(const char __user *pathname, char __user *list, size_t size) { - struct nameidata nd; + struct path path; ssize_t error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) return error; - error = listxattr(nd.path.dentry, list, size); - path_put(&nd.path); + error = listxattr(path.dentry, list, size); + path_put(&path); return error; } asmlinkage ssize_t -sys_llistxattr(const char __user *path, char __user *list, size_t size) +sys_llistxattr(const char __user *pathname, char __user *list, size_t size) { - struct nameidata nd; + struct path path; ssize_t error; - error = user_path_walk_link(path, &nd); + error = user_lpath(pathname, &path); if (error) return error; - error = listxattr(nd.path.dentry, list, size); - path_put(&nd.path); + error = listxattr(path.dentry, list, size); + path_put(&path); return error; } @@ -486,38 +486,38 @@ removexattr(struct dentry *d, const char __user *name) } asmlinkage long -sys_removexattr(const char __user *path, const char __user *name) +sys_removexattr(const char __user *pathname, const char __user *name) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk(path, &nd); + error = user_path(pathname, &path); if (error) return error; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (!error) { - error = removexattr(nd.path.dentry, name); - mnt_drop_write(nd.path.mnt); + error = removexattr(path.dentry, name); + mnt_drop_write(path.mnt); } - path_put(&nd.path); + path_put(&path); return error; } asmlinkage long -sys_lremovexattr(const char __user *path, const char __user *name) +sys_lremovexattr(const char __user *pathname, const char __user *name) { - struct nameidata nd; + struct path path; int error; - error = user_path_walk_link(path, &nd); + error = user_lpath(pathname, &path); if (error) return error; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (!error) { - error = removexattr(nd.path.dentry, name); - mnt_drop_write(nd.path.mnt); + error = removexattr(path.dentry, name); + mnt_drop_write(path.mnt); } - path_put(&nd.path); + path_put(&path); return error; } diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index a42ba9d71156..01939ba2d8de 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -84,17 +84,15 @@ xfs_find_handle( switch (cmd) { case XFS_IOC_PATH_TO_FSHANDLE: case XFS_IOC_PATH_TO_HANDLE: { - struct nameidata nd; - int error; - - error = user_path_walk_link((const char __user *)hreq.path, &nd); + struct path path; + int error = user_lpath((const char __user *)hreq.path, &path); if (error) return error; - ASSERT(nd.path.dentry); - ASSERT(nd.path.dentry->d_inode); - inode = igrab(nd.path.dentry->d_inode); - path_put(&nd.path); + ASSERT(path.dentry); + ASSERT(path.dentry->d_inode); + inode = igrab(path.dentry->d_inode); + path_put(&path); break; } diff --git a/include/linux/namei.h b/include/linux/namei.h index 60e35a02f6cb..00888ff69504 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -54,12 +54,13 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; #define LOOKUP_OPEN (0x0100) #define LOOKUP_CREATE (0x0200) -extern int __user_walk(const char __user *, unsigned, struct nameidata *); -extern int __user_walk_fd(int dfd, const char __user *, unsigned, struct nameidata *); -#define user_path_walk(name,nd) \ - __user_walk_fd(AT_FDCWD, name, LOOKUP_FOLLOW, nd) -#define user_path_walk_link(name,nd) \ - __user_walk_fd(AT_FDCWD, name, 0, nd) +extern int user_path_at(int, const char __user *, unsigned, struct path *); + +#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path) +#define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path) +#define user_path_dir(name, path) \ + user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path) + extern int path_lookup(const char *, unsigned, struct nameidata *); extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct nameidata *); -- cgit From 516e0cc5646f377ab80fcc2ee639892eccb99853 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Jul 2008 00:39:17 -0400 Subject: [PATCH] f_count may wrap around make it atomic_long_t; while we are at it, get rid of useless checks in affs, hfs and hpfs - ->open() always has it equal to 1, ->release() - to 0. Signed-off-by: Al Viro --- drivers/net/ppp_generic.c | 6 +++--- fs/affs/file.c | 4 ---- fs/aio.c | 6 +++--- fs/file_table.c | 10 +++++----- fs/hfs/inode.c | 4 ---- fs/hfsplus/inode.c | 4 ---- include/linux/fs.h | 6 +++--- include/net/af_unix.h | 2 +- net/sched/sch_atm.c | 4 ++-- net/unix/af_unix.c | 2 +- net/unix/garbage.c | 18 +++++++++--------- 11 files changed, 27 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 739b3ab7bccc..ddccc074a76a 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -581,12 +581,12 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (file == ppp->owner) ppp_shutdown_interface(ppp); } - if (atomic_read(&file->f_count) <= 2) { + if (atomic_long_read(&file->f_count) <= 2) { ppp_release(NULL, file); err = 0; } else - printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%d\n", - atomic_read(&file->f_count)); + printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%ld\n", + atomic_long_read(&file->f_count)); unlock_kernel(); return err; } diff --git a/fs/affs/file.c b/fs/affs/file.c index 6eac7bdeec94..1377b1240b6e 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -46,8 +46,6 @@ const struct inode_operations affs_file_inode_operations = { static int affs_file_open(struct inode *inode, struct file *filp) { - if (atomic_read(&filp->f_count) != 1) - return 0; pr_debug("AFFS: open(%lu,%d)\n", inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt)); atomic_inc(&AFFS_I(inode)->i_opencnt); @@ -57,8 +55,6 @@ affs_file_open(struct inode *inode, struct file *filp) static int affs_file_release(struct inode *inode, struct file *filp) { - if (atomic_read(&filp->f_count) != 0) - return 0; pr_debug("AFFS: release(%lu, %d)\n", inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt)); diff --git a/fs/aio.c b/fs/aio.c index 0051fd94b44e..f658441d5666 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -512,8 +512,8 @@ static void aio_fput_routine(struct work_struct *data) */ static int __aio_put_req(struct kioctx *ctx, struct kiocb *req) { - dprintk(KERN_DEBUG "aio_put(%p): f_count=%d\n", - req, atomic_read(&req->ki_filp->f_count)); + dprintk(KERN_DEBUG "aio_put(%p): f_count=%ld\n", + req, atomic_long_read(&req->ki_filp->f_count)); assert_spin_locked(&ctx->ctx_lock); @@ -528,7 +528,7 @@ static int __aio_put_req(struct kioctx *ctx, struct kiocb *req) /* Must be done under the lock to serialise against cancellation. * Call this aio_fput as it duplicates fput via the fput_work. */ - if (unlikely(atomic_dec_and_test(&req->ki_filp->f_count))) { + if (unlikely(atomic_long_dec_and_test(&req->ki_filp->f_count))) { get_ioctx(ctx); spin_lock(&fput_lock); list_add(&req->ki_list, &fput_head); diff --git a/fs/file_table.c b/fs/file_table.c index 83084225b4c3..f45a4493f9e7 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -120,7 +120,7 @@ struct file *get_empty_filp(void) tsk = current; INIT_LIST_HEAD(&f->f_u.fu_list); - atomic_set(&f->f_count, 1); + atomic_long_set(&f->f_count, 1); rwlock_init(&f->f_owner.lock); f->f_uid = tsk->fsuid; f->f_gid = tsk->fsgid; @@ -219,7 +219,7 @@ EXPORT_SYMBOL(init_file); void fput(struct file *file) { - if (atomic_dec_and_test(&file->f_count)) + if (atomic_long_dec_and_test(&file->f_count)) __fput(file); } @@ -294,7 +294,7 @@ struct file *fget(unsigned int fd) rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - if (!atomic_inc_not_zero(&file->f_count)) { + if (!atomic_long_inc_not_zero(&file->f_count)) { /* File object ref couldn't be taken */ rcu_read_unlock(); return NULL; @@ -326,7 +326,7 @@ struct file *fget_light(unsigned int fd, int *fput_needed) rcu_read_lock(); file = fcheck_files(files, fd); if (file) { - if (atomic_inc_not_zero(&file->f_count)) + if (atomic_long_inc_not_zero(&file->f_count)) *fput_needed = 1; else /* Didn't get the reference, someone's freed */ @@ -341,7 +341,7 @@ struct file *fget_light(unsigned int fd, int *fput_needed) void put_filp(struct file *file) { - if (atomic_dec_and_test(&file->f_count)) { + if (atomic_long_dec_and_test(&file->f_count)) { security_file_free(file); file_kill(file); file_free(file); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index aa73f3fd5dd9..7e19835efa2e 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -522,8 +522,6 @@ static int hfs_file_open(struct inode *inode, struct file *file) { if (HFS_IS_RSRC(inode)) inode = HFS_I(inode)->rsrc_inode; - if (atomic_read(&file->f_count) != 1) - return 0; atomic_inc(&HFS_I(inode)->opencnt); return 0; } @@ -534,8 +532,6 @@ static int hfs_file_release(struct inode *inode, struct file *file) if (HFS_IS_RSRC(inode)) inode = HFS_I(inode)->rsrc_inode; - if (atomic_read(&file->f_count) != 0) - return 0; if (atomic_dec_and_test(&HFS_I(inode)->opencnt)) { mutex_lock(&inode->i_mutex); hfs_file_truncate(inode); diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index d4014e3044d2..b085d64a2b67 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -254,8 +254,6 @@ static int hfsplus_file_open(struct inode *inode, struct file *file) { if (HFSPLUS_IS_RSRC(inode)) inode = HFSPLUS_I(inode).rsrc_inode; - if (atomic_read(&file->f_count) != 1) - return 0; atomic_inc(&HFSPLUS_I(inode).opencnt); return 0; } @@ -266,8 +264,6 @@ static int hfsplus_file_release(struct inode *inode, struct file *file) if (HFSPLUS_IS_RSRC(inode)) inode = HFSPLUS_I(inode).rsrc_inode; - if (atomic_read(&file->f_count) != 0) - return 0; if (atomic_dec_and_test(&HFSPLUS_I(inode).opencnt)) { mutex_lock(&inode->i_mutex); hfsplus_file_truncate(inode); diff --git a/include/linux/fs.h b/include/linux/fs.h index 9d2de4cadabd..7676fa1c20ae 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -795,7 +795,7 @@ struct file { #define f_dentry f_path.dentry #define f_vfsmnt f_path.mnt const struct file_operations *f_op; - atomic_t f_count; + atomic_long_t f_count; unsigned int f_flags; mode_t f_mode; loff_t f_pos; @@ -824,8 +824,8 @@ extern spinlock_t files_lock; #define file_list_lock() spin_lock(&files_lock); #define file_list_unlock() spin_unlock(&files_lock); -#define get_file(x) atomic_inc(&(x)->f_count) -#define file_count(x) atomic_read(&(x)->f_count) +#define get_file(x) atomic_long_inc(&(x)->f_count) +#define file_count(x) atomic_long_read(&(x)->f_count) #ifdef CONFIG_DEBUG_WRITECOUNT static inline void file_take_write(struct file *f) diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 2dfa96b0575e..7dd29b7e461d 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -51,7 +51,7 @@ struct unix_sock { struct sock *peer; struct sock *other; struct list_head link; - atomic_t inflight; + atomic_long_t inflight; spinlock_t lock; unsigned int gc_candidate : 1; wait_queue_head_t peer_wait; diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 04faa835be17..6b517b9dac5b 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -162,7 +162,7 @@ static void atm_tc_put(struct Qdisc *sch, unsigned long cl) qdisc_destroy(flow->q); tcf_destroy_chain(&flow->filter_list); if (flow->sock) { - pr_debug("atm_tc_put: f_count %d\n", + pr_debug("atm_tc_put: f_count %ld\n", file_count(flow->sock->file)); flow->vcc->pop = flow->old_pop; sockfd_put(flow->sock); @@ -259,7 +259,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, sock = sockfd_lookup(fd, &error); if (!sock) return error; /* f_count++ */ - pr_debug("atm_tc_change: f_count %d\n", file_count(sock->file)); + pr_debug("atm_tc_change: f_count %ld\n", file_count(sock->file)); if (sock->ops->family != PF_ATMSVC && sock->ops->family != PF_ATMPVC) { error = -EPROTOTYPE; goto err_out; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 70ceb1604ad8..6e7fec74bdb3 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -603,7 +603,7 @@ static struct sock * unix_create1(struct net *net, struct socket *sock) u->dentry = NULL; u->mnt = NULL; spin_lock_init(&u->lock); - atomic_set(&u->inflight, 0); + atomic_long_set(&u->inflight, 0); INIT_LIST_HEAD(&u->link); mutex_init(&u->readlock); /* single task reading lock */ init_waitqueue_head(&u->peer_wait); diff --git a/net/unix/garbage.c b/net/unix/garbage.c index ebdff3d877a1..2a27b84f740b 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -127,7 +127,7 @@ void unix_inflight(struct file *fp) if(s) { struct unix_sock *u = unix_sk(s); spin_lock(&unix_gc_lock); - if (atomic_inc_return(&u->inflight) == 1) { + if (atomic_long_inc_return(&u->inflight) == 1) { BUG_ON(!list_empty(&u->link)); list_add_tail(&u->link, &gc_inflight_list); } else { @@ -145,7 +145,7 @@ void unix_notinflight(struct file *fp) struct unix_sock *u = unix_sk(s); spin_lock(&unix_gc_lock); BUG_ON(list_empty(&u->link)); - if (atomic_dec_and_test(&u->inflight)) + if (atomic_long_dec_and_test(&u->inflight)) list_del_init(&u->link); unix_tot_inflight--; spin_unlock(&unix_gc_lock); @@ -237,17 +237,17 @@ static void scan_children(struct sock *x, void (*func)(struct unix_sock *), static void dec_inflight(struct unix_sock *usk) { - atomic_dec(&usk->inflight); + atomic_long_dec(&usk->inflight); } static void inc_inflight(struct unix_sock *usk) { - atomic_inc(&usk->inflight); + atomic_long_inc(&usk->inflight); } static void inc_inflight_move_tail(struct unix_sock *u) { - atomic_inc(&u->inflight); + atomic_long_inc(&u->inflight); /* * If this is still a candidate, move it to the end of the * list, so that it's checked even if it was already passed @@ -288,11 +288,11 @@ void unix_gc(void) * before the detach without atomicity guarantees. */ list_for_each_entry_safe(u, next, &gc_inflight_list, link) { - int total_refs; - int inflight_refs; + long total_refs; + long inflight_refs; total_refs = file_count(u->sk.sk_socket->file); - inflight_refs = atomic_read(&u->inflight); + inflight_refs = atomic_long_read(&u->inflight); BUG_ON(inflight_refs < 1); BUG_ON(total_refs < inflight_refs); @@ -324,7 +324,7 @@ void unix_gc(void) /* Move cursor to after the current position. */ list_move(&cursor, &u->link); - if (atomic_read(&u->inflight) > 0) { + if (atomic_long_read(&u->inflight) > 0) { list_move_tail(&u->link, &gc_inflight_list); u->gc_candidate = 0; scan_children(&u->sk, inc_inflight_move_tail, NULL); -- cgit From 964bd183624c03680796b63b4ab97ee3905a806a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Jul 2008 03:33:14 -0400 Subject: [PATCH] get rid of __user_path_lookup_open Signed-off-by: Al Viro --- fs/exec.c | 14 ++++++++++---- fs/namei.c | 13 ------------- include/linux/namei.h | 1 - 3 files changed, 10 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index eca58c29eded..9696bbf0f0b1 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -106,11 +106,17 @@ static inline void put_binfmt(struct linux_binfmt * fmt) */ asmlinkage long sys_uselib(const char __user * library) { - struct file * file; + struct file *file; struct nameidata nd; - int error; - - error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC); + char *tmp = getname(library); + int error = PTR_ERR(tmp); + + if (!IS_ERR(tmp)) { + error = path_lookup_open(AT_FDCWD, tmp, + LOOKUP_FOLLOW, &nd, + FMODE_READ|FMODE_EXEC); + putname(tmp); + } if (error) goto out; diff --git a/fs/namei.c b/fs/namei.c index 38ceb6e06eba..a7b0a0b80128 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1193,19 +1193,6 @@ static int path_lookup_create(int dfd, const char *name, nd, open_flags, create_mode); } -int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags, - struct nameidata *nd, int open_flags) -{ - char *tmp = getname(name); - int err = PTR_ERR(tmp); - - if (!IS_ERR(tmp)) { - err = __path_lookup_intent_open(AT_FDCWD, tmp, lookup_flags, nd, open_flags, 0); - putname(tmp); - } - return err; -} - static struct dentry *__lookup_hash(struct qstr *name, struct dentry *base, struct nameidata *nd) { diff --git a/include/linux/namei.h b/include/linux/namei.h index 00888ff69504..68f8c3203c89 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -65,7 +65,6 @@ extern int path_lookup(const char *, unsigned, struct nameidata *); extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct nameidata *); -extern int __user_path_lookup_open(const char __user *, unsigned lookup_flags, struct nameidata *nd, int open_flags); extern int path_lookup_open(int dfd, const char *name, unsigned lookup_flags, struct nameidata *, int open_flags); extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, int (*open)(struct inode *, struct file *)); -- cgit From 3f8206d496e9e9495afb1d4e70d29712b4d403c9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 26 Jul 2008 03:46:43 -0400 Subject: [PATCH] get rid of indirect users of namei.h fs.h needs path.h, not namei.h; nfs_fs.h doesn't need it at all. Several places in the tree needed direct include. Signed-off-by: Al Viro --- fs/nfsd/nfsctl.c | 1 + fs/ubifs/file.c | 1 + include/linux/fs.h | 2 +- include/linux/nfs_fs.h | 1 - kernel/cgroup.c | 1 + 5 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 1955a2702e60..c53e65f8f3a2 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 005a3b854d96..8565e586e533 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -53,6 +53,7 @@ #include "ubifs.h" #include +#include static int read_block(struct inode *inode, void *addr, unsigned int block, struct ubifs_data_node *dn) diff --git a/include/linux/fs.h b/include/linux/fs.h index 7676fa1c20ae..8252b045e624 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -279,7 +279,7 @@ extern int dir_notify_enable; #include #include #include -#include +#include #include #include #include diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index f08f9ca602af..78a5922a2f11 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 89bd6fb7894f..657f8f8d93a5 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -45,6 +45,7 @@ #include #include #include +#include #include -- cgit From 510a35d4a47802f4a0028aa6bd2ca2170da5e32f Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Sat, 26 Jul 2008 15:22:27 -0700 Subject: hugetlb: remove unused variable warning Remove the following warning when CONFIG_HUGETLB_PAGE is not set: ipc/shm.c: In function `shm_get_stat': ipc/shm.c:565: warning: unused variable `h' [akpm@linux-foundation.org: use tabs, not spaces] Signed-off-by: Andrea Righi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 9a71d4cc88c8..32e0ef0f6e1f 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -273,7 +273,10 @@ struct hstate {}; #define huge_page_mask(h) PAGE_MASK #define huge_page_order(h) 0 #define huge_page_shift(h) PAGE_SHIFT -#define pages_per_huge_page(h) 1 +static inline unsigned int pages_per_huge_page(struct hstate *h) +{ + return 1; +} #endif #endif /* _LINUX_HUGETLB_H */ -- cgit From 9993e51c0c47ec69dce1f26c2321af6bb9165e9e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 26 Jul 2008 13:53:46 -0300 Subject: V4L/DVB (8502): videodev2.h: CodingStyle cleanups Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 378 ++++++++++++++++++++-------------------------- 1 file changed, 166 insertions(+), 212 deletions(-) (limited to 'include/linux') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 2e66a95e8d32..cc0c8952323b 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -91,8 +91,8 @@ */ /* Four-character-code (FOURCC) */ -#define v4l2_fourcc(a,b,c,d)\ - (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24)) +#define v4l2_fourcc(a, b, c, d)\ + ((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24)) /* * E N U M S @@ -226,8 +226,7 @@ struct v4l2_fract { /* * D R I V E R C A P A B I L I T I E S */ -struct v4l2_capability -{ +struct v4l2_capability { __u8 driver[16]; /* i.e. "bttv" */ __u8 card[32]; /* i.e. "Hauppauge WinTV" */ __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */ @@ -259,8 +258,7 @@ struct v4l2_capability /* * V I D E O I M A G E F O R M A T */ -struct v4l2_pix_format -{ +struct v4l2_pix_format { __u32 width; __u32 height; __u32 pixelformat; @@ -272,68 +270,67 @@ struct v4l2_pix_format }; /* Pixel format FOURCC depth Description */ -#define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R','G','B','1') /* 8 RGB-3-3-2 */ -#define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R','4','4','4') /* 16 xxxxrrrr ggggbbbb */ -#define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R','G','B','O') /* 16 RGB-5-5-5 */ -#define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R','G','B','P') /* 16 RGB-5-6-5 */ -#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R','G','B','Q') /* 16 RGB-5-5-5 BE */ -#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R','G','B','R') /* 16 RGB-5-6-5 BE */ -#define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B','G','R','3') /* 24 BGR-8-8-8 */ -#define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R','G','B','3') /* 24 RGB-8-8-8 */ -#define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B','G','R','4') /* 32 BGR-8-8-8-8 */ -#define V4L2_PIX_FMT_RGB32 v4l2_fourcc('R','G','B','4') /* 32 RGB-8-8-8-8 */ -#define V4L2_PIX_FMT_GREY v4l2_fourcc('G','R','E','Y') /* 8 Greyscale */ -#define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y','1','6',' ') /* 16 Greyscale */ -#define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P','A','L','8') /* 8 8-bit palette */ -#define V4L2_PIX_FMT_YVU410 v4l2_fourcc('Y','V','U','9') /* 9 YVU 4:1:0 */ -#define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y','V','1','2') /* 12 YVU 4:2:0 */ -#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y','U','Y','V') /* 16 YUV 4:2:2 */ -#define V4L2_PIX_FMT_UYVY v4l2_fourcc('U','Y','V','Y') /* 16 YUV 4:2:2 */ -#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4','2','2','P') /* 16 YVU422 planar */ -#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4','1','1','P') /* 16 YVU411 planar */ -#define V4L2_PIX_FMT_Y41P v4l2_fourcc('Y','4','1','P') /* 12 YUV 4:1:1 */ -#define V4L2_PIX_FMT_YUV444 v4l2_fourcc('Y','4','4','4') /* 16 xxxxyyyy uuuuvvvv */ -#define V4L2_PIX_FMT_YUV555 v4l2_fourcc('Y','U','V','O') /* 16 YUV-5-5-5 */ -#define V4L2_PIX_FMT_YUV565 v4l2_fourcc('Y','U','V','P') /* 16 YUV-5-6-5 */ -#define V4L2_PIX_FMT_YUV32 v4l2_fourcc('Y','U','V','4') /* 32 YUV-8-8-8-8 */ +#define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R', 'G', 'B', '1') /* 8 RGB-3-3-2 */ +#define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R', '4', '4', '4') /* 16 xxxxrrrr ggggbbbb */ +#define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R', 'G', 'B', 'O') /* 16 RGB-5-5-5 */ +#define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */ +#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */ +#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */ +#define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */ +#define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */ +#define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */ +#define V4L2_PIX_FMT_RGB32 v4l2_fourcc('R', 'G', 'B', '4') /* 32 RGB-8-8-8-8 */ +#define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */ +#define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y', '1', '6', ' ') /* 16 Greyscale */ +#define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P', 'A', 'L', '8') /* 8 8-bit palette */ +#define V4L2_PIX_FMT_YVU410 v4l2_fourcc('Y', 'V', 'U', '9') /* 9 YVU 4:1:0 */ +#define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y', 'V', '1', '2') /* 12 YVU 4:2:0 */ +#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_UYVY v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4', '2', '2', 'P') /* 16 YVU422 planar */ +#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4', '1', '1', 'P') /* 16 YVU411 planar */ +#define V4L2_PIX_FMT_Y41P v4l2_fourcc('Y', '4', '1', 'P') /* 12 YUV 4:1:1 */ +#define V4L2_PIX_FMT_YUV444 v4l2_fourcc('Y', '4', '4', '4') /* 16 xxxxyyyy uuuuvvvv */ +#define V4L2_PIX_FMT_YUV555 v4l2_fourcc('Y', 'U', 'V', 'O') /* 16 YUV-5-5-5 */ +#define V4L2_PIX_FMT_YUV565 v4l2_fourcc('Y', 'U', 'V', 'P') /* 16 YUV-5-6-5 */ +#define V4L2_PIX_FMT_YUV32 v4l2_fourcc('Y', 'U', 'V', '4') /* 32 YUV-8-8-8-8 */ /* two planes -- one Y, one Cr + Cb interleaved */ -#define V4L2_PIX_FMT_NV12 v4l2_fourcc('N','V','1','2') /* 12 Y/CbCr 4:2:0 */ -#define V4L2_PIX_FMT_NV21 v4l2_fourcc('N','V','2','1') /* 12 Y/CrCb 4:2:0 */ +#define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */ +#define V4L2_PIX_FMT_NV21 v4l2_fourcc('N', 'V', '2', '1') /* 12 Y/CrCb 4:2:0 */ /* The following formats are not defined in the V4L2 specification */ -#define V4L2_PIX_FMT_YUV410 v4l2_fourcc('Y','U','V','9') /* 9 YUV 4:1:0 */ -#define V4L2_PIX_FMT_YUV420 v4l2_fourcc('Y','U','1','2') /* 12 YUV 4:2:0 */ -#define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y','Y','U','V') /* 16 YUV 4:2:2 */ -#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H','I','2','4') /* 8 8-bit color */ -#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H','M','1','2') /* 8 YUV 4:2:0 16x16 macroblocks */ +#define V4L2_PIX_FMT_YUV410 v4l2_fourcc('Y', 'U', 'V', '9') /* 9 YUV 4:1:0 */ +#define V4L2_PIX_FMT_YUV420 v4l2_fourcc('Y', 'U', '1', '2') /* 12 YUV 4:2:0 */ +#define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y', 'Y', 'U', 'V') /* 16 YUV 4:2:2 */ +#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* 8 8-bit color */ +#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2') /* 8 YUV 4:2:0 16x16 macroblocks */ /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ -#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ -#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G','B','R','G') /* 8 GBGB.. RGRG.. */ -#define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B','Y','R','2') /* 16 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */ +#define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16 BGBG.. GRGR.. */ /* compressed formats */ -#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M','J','P','G') /* Motion-JPEG */ -#define V4L2_PIX_FMT_JPEG v4l2_fourcc('J','P','E','G') /* JFIF JPEG */ -#define V4L2_PIX_FMT_DV v4l2_fourcc('d','v','s','d') /* 1394 */ -#define V4L2_PIX_FMT_MPEG v4l2_fourcc('M','P','E','G') /* MPEG-1/2/4 */ +#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG */ +#define V4L2_PIX_FMT_JPEG v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG */ +#define V4L2_PIX_FMT_DV v4l2_fourcc('d', 'v', 's', 'd') /* 1394 */ +#define V4L2_PIX_FMT_MPEG v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4 */ /* Vendor-specific formats */ -#define V4L2_PIX_FMT_WNVA v4l2_fourcc('W','N','V','A') /* Winnov hw compress */ -#define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S','9','1','0') /* SN9C10x compression */ -#define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') /* pwc older webcam */ -#define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') /* pwc newer webcam */ -#define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E','6','2','5') /* ET61X251 compression */ -#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') /* YUYV per line */ -#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S','5','6','1') /* compressed GBRG bayer */ -#define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P','2','0','7') /* compressed BGGR bayer */ +#define V4L2_PIX_FMT_WNVA v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */ +#define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */ +#define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P', 'W', 'C', '1') /* pwc older webcam */ +#define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */ +#define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */ +#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */ +#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ +#define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ /* * F O R M A T E N U M E R A T I O N */ -struct v4l2_fmtdesc -{ +struct v4l2_fmtdesc { __u32 index; /* Format number */ enum v4l2_buf_type type; /* buffer type */ __u32 flags; @@ -349,21 +346,18 @@ struct v4l2_fmtdesc /* * F R A M E S I Z E E N U M E R A T I O N */ -enum v4l2_frmsizetypes -{ +enum v4l2_frmsizetypes { V4L2_FRMSIZE_TYPE_DISCRETE = 1, V4L2_FRMSIZE_TYPE_CONTINUOUS = 2, V4L2_FRMSIZE_TYPE_STEPWISE = 3, }; -struct v4l2_frmsize_discrete -{ +struct v4l2_frmsize_discrete { __u32 width; /* Frame width [pixel] */ __u32 height; /* Frame height [pixel] */ }; -struct v4l2_frmsize_stepwise -{ +struct v4l2_frmsize_stepwise { __u32 min_width; /* Minimum frame width [pixel] */ __u32 max_width; /* Maximum frame width [pixel] */ __u32 step_width; /* Frame width step size [pixel] */ @@ -372,8 +366,7 @@ struct v4l2_frmsize_stepwise __u32 step_height; /* Frame height step size [pixel] */ }; -struct v4l2_frmsizeenum -{ +struct v4l2_frmsizeenum { __u32 index; /* Frame size number */ __u32 pixel_format; /* Pixel format */ __u32 type; /* Frame size type the device supports. */ @@ -389,22 +382,19 @@ struct v4l2_frmsizeenum /* * F R A M E R A T E E N U M E R A T I O N */ -enum v4l2_frmivaltypes -{ +enum v4l2_frmivaltypes { V4L2_FRMIVAL_TYPE_DISCRETE = 1, V4L2_FRMIVAL_TYPE_CONTINUOUS = 2, V4L2_FRMIVAL_TYPE_STEPWISE = 3, }; -struct v4l2_frmival_stepwise -{ +struct v4l2_frmival_stepwise { struct v4l2_fract min; /* Minimum frame interval [s] */ struct v4l2_fract max; /* Maximum frame interval [s] */ struct v4l2_fract step; /* Frame interval step size [s] */ }; -struct v4l2_frmivalenum -{ +struct v4l2_frmivalenum { __u32 index; /* Frame format index */ __u32 pixel_format; /* Pixel format */ __u32 width; /* Frame width */ @@ -423,8 +413,7 @@ struct v4l2_frmivalenum /* * T I M E C O D E */ -struct v4l2_timecode -{ +struct v4l2_timecode { __u32 type; __u32 flags; __u8 frames; @@ -449,8 +438,7 @@ struct v4l2_timecode #define V4L2_TC_USERBITS_8BITCHARS 0x0008 /* The above is based on SMPTE timecodes */ -struct v4l2_jpegcompression -{ +struct v4l2_jpegcompression { int quality; int APPn; /* Number of APP segment to be written, @@ -482,16 +470,14 @@ struct v4l2_jpegcompression /* * M E M O R Y - M A P P I N G B U F F E R S */ -struct v4l2_requestbuffers -{ +struct v4l2_requestbuffers { __u32 count; enum v4l2_buf_type type; enum v4l2_memory memory; __u32 reserved[2]; }; -struct v4l2_buffer -{ +struct v4l2_buffer { __u32 index; enum v4l2_buf_type type; __u32 bytesused; @@ -525,13 +511,12 @@ struct v4l2_buffer /* * O V E R L A Y P R E V I E W */ -struct v4l2_framebuffer -{ +struct v4l2_framebuffer { __u32 capability; __u32 flags; /* FIXME: in theory we should pass something like PCI device + memory * region + offset instead of some physical address */ - void* base; + void *base; struct v4l2_pix_format fmt; }; /* Flags for the 'capability' field. Read only */ @@ -550,14 +535,12 @@ struct v4l2_framebuffer #define V4L2_FBUF_FLAG_GLOBAL_ALPHA 0x0010 #define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA 0x0020 -struct v4l2_clip -{ +struct v4l2_clip { struct v4l2_rect c; struct v4l2_clip __user *next; }; -struct v4l2_window -{ +struct v4l2_window { struct v4l2_rect w; enum v4l2_field field; __u32 chromakey; @@ -570,8 +553,7 @@ struct v4l2_window /* * C A P T U R E P A R A M E T E R S */ -struct v4l2_captureparm -{ +struct v4l2_captureparm { __u32 capability; /* Supported modes */ __u32 capturemode; /* Current mode */ struct v4l2_fract timeperframe; /* Time per frame in .1us units */ @@ -584,8 +566,7 @@ struct v4l2_captureparm #define V4L2_MODE_HIGHQUALITY 0x0001 /* High quality imaging mode */ #define V4L2_CAP_TIMEPERFRAME 0x1000 /* timeperframe field is supported */ -struct v4l2_outputparm -{ +struct v4l2_outputparm { __u32 capability; /* Supported modes */ __u32 outputmode; /* Current mode */ struct v4l2_fract timeperframe; /* Time per frame in seconds */ @@ -702,8 +683,7 @@ typedef __u64 v4l2_std_id; #define V4L2_STD_ALL (V4L2_STD_525_60 |\ V4L2_STD_625_50) -struct v4l2_standard -{ +struct v4l2_standard { __u32 index; v4l2_std_id id; __u8 name[24]; @@ -715,8 +695,7 @@ struct v4l2_standard /* * V I D E O I N P U T S */ -struct v4l2_input -{ +struct v4l2_input { __u32 index; /* Which input */ __u8 name[32]; /* Label */ __u32 type; /* Type of input */ @@ -753,8 +732,7 @@ struct v4l2_input /* * V I D E O O U T P U T S */ -struct v4l2_output -{ +struct v4l2_output { __u32 index; /* Which output */ __u8 name[32]; /* Label */ __u32 type; /* Type of output */ @@ -771,14 +749,12 @@ struct v4l2_output /* * C O N T R O L S */ -struct v4l2_control -{ +struct v4l2_control { __u32 id; __s32 value; }; -struct v4l2_ext_control -{ +struct v4l2_ext_control { __u32 id; __u32 reserved2[2]; union { @@ -788,8 +764,7 @@ struct v4l2_ext_control }; } __attribute__ ((packed)); -struct v4l2_ext_controls -{ +struct v4l2_ext_controls { __u32 ctrl_class; __u32 count; __u32 error_idx; @@ -807,8 +782,7 @@ struct v4l2_ext_controls #define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000) /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ -struct v4l2_queryctrl -{ +struct v4l2_queryctrl { __u32 id; enum v4l2_ctrl_type type; __u8 name[32]; /* Whatever */ @@ -821,8 +795,7 @@ struct v4l2_queryctrl }; /* Used in the VIDIOC_QUERYMENU ioctl for querying menu items */ -struct v4l2_querymenu -{ +struct v4l2_querymenu { __u32 id; __u32 index; __u8 name[32]; /* Whatever */ @@ -1104,8 +1077,7 @@ enum v4l2_exposure_auto_type { /* * T U N I N G */ -struct v4l2_tuner -{ +struct v4l2_tuner { __u32 index; __u8 name[32]; enum v4l2_tuner_type type; @@ -1119,8 +1091,7 @@ struct v4l2_tuner __u32 reserved[4]; }; -struct v4l2_modulator -{ +struct v4l2_modulator { __u32 index; __u8 name[32]; __u32 capability; @@ -1153,8 +1124,7 @@ struct v4l2_modulator #define V4L2_TUNER_MODE_LANG1 0x0003 #define V4L2_TUNER_MODE_LANG1_LANG2 0x0004 -struct v4l2_frequency -{ +struct v4l2_frequency { __u32 tuner; enum v4l2_tuner_type type; __u32 frequency; @@ -1172,8 +1142,7 @@ struct v4l2_hw_freq_seek { /* * A U D I O */ -struct v4l2_audio -{ +struct v4l2_audio { __u32 index; __u8 name[32]; __u32 capability; @@ -1188,8 +1157,7 @@ struct v4l2_audio /* Flags for the 'mode' field */ #define V4L2_AUDMODE_AVL 0x00001 -struct v4l2_audioout -{ +struct v4l2_audioout { __u32 index; __u8 name[32]; __u32 capability; @@ -1253,8 +1221,7 @@ struct v4l2_encoder_cmd { */ /* Raw VBI */ -struct v4l2_vbi_format -{ +struct v4l2_vbi_format { __u32 sampling_rate; /* in 1 Hz */ __u32 offset; __u32 samples_per_line; @@ -1266,8 +1233,8 @@ struct v4l2_vbi_format }; /* VBI flags */ -#define V4L2_VBI_UNSYNC (1<< 0) -#define V4L2_VBI_INTERLACED (1<< 1) +#define V4L2_VBI_UNSYNC (1 << 0) +#define V4L2_VBI_INTERLACED (1 << 1) /* Sliced VBI * @@ -1276,8 +1243,7 @@ struct v4l2_vbi_format * notice in the definitive implementation. */ -struct v4l2_sliced_vbi_format -{ +struct v4l2_sliced_vbi_format { __u16 service_set; /* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field @@ -1301,8 +1267,7 @@ struct v4l2_sliced_vbi_format #define V4L2_SLICED_VBI_525 (V4L2_SLICED_CAPTION_525) #define V4L2_SLICED_VBI_625 (V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625) -struct v4l2_sliced_vbi_cap -{ +struct v4l2_sliced_vbi_cap { __u16 service_set; /* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field @@ -1313,8 +1278,7 @@ struct v4l2_sliced_vbi_cap __u32 reserved[3]; /* must be 0 */ }; -struct v4l2_sliced_vbi_data -{ +struct v4l2_sliced_vbi_data { __u32 id; __u32 field; /* 0: first field, 1: second field */ __u32 line; /* 1-23 */ @@ -1328,27 +1292,23 @@ struct v4l2_sliced_vbi_data /* Stream data format */ -struct v4l2_format -{ +struct v4l2_format { enum v4l2_buf_type type; - union - { - struct v4l2_pix_format pix; // V4L2_BUF_TYPE_VIDEO_CAPTURE - struct v4l2_window win; // V4L2_BUF_TYPE_VIDEO_OVERLAY - struct v4l2_vbi_format vbi; // V4L2_BUF_TYPE_VBI_CAPTURE - struct v4l2_sliced_vbi_format sliced; // V4L2_BUF_TYPE_SLICED_VBI_CAPTURE - __u8 raw_data[200]; // user-defined + union { + struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ + struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ + struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */ + struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */ + __u8 raw_data[200]; /* user-defined */ } fmt; }; /* Stream type-dependent parameters */ -struct v4l2_streamparm -{ +struct v4l2_streamparm { enum v4l2_buf_type type; - union - { + union { struct v4l2_captureparm capture; struct v4l2_outputparm output; __u8 raw_data[200]; /* user-defined */ @@ -1386,92 +1346,86 @@ struct v4l2_chip_ident { * I O C T L C O D E S F O R V I D E O D E V I C E S * */ -#define VIDIOC_QUERYCAP _IOR ('V', 0, struct v4l2_capability) -#define VIDIOC_RESERVED _IO ('V', 1) -#define VIDIOC_ENUM_FMT _IOWR ('V', 2, struct v4l2_fmtdesc) -#define VIDIOC_G_FMT _IOWR ('V', 4, struct v4l2_format) -#define VIDIOC_S_FMT _IOWR ('V', 5, struct v4l2_format) -#define VIDIOC_REQBUFS _IOWR ('V', 8, struct v4l2_requestbuffers) -#define VIDIOC_QUERYBUF _IOWR ('V', 9, struct v4l2_buffer) -#define VIDIOC_G_FBUF _IOR ('V', 10, struct v4l2_framebuffer) -#define VIDIOC_S_FBUF _IOW ('V', 11, struct v4l2_framebuffer) -#define VIDIOC_OVERLAY _IOW ('V', 14, int) -#define VIDIOC_QBUF _IOWR ('V', 15, struct v4l2_buffer) -#define VIDIOC_DQBUF _IOWR ('V', 17, struct v4l2_buffer) -#define VIDIOC_STREAMON _IOW ('V', 18, int) -#define VIDIOC_STREAMOFF _IOW ('V', 19, int) -#define VIDIOC_G_PARM _IOWR ('V', 21, struct v4l2_streamparm) -#define VIDIOC_S_PARM _IOWR ('V', 22, struct v4l2_streamparm) -#define VIDIOC_G_STD _IOR ('V', 23, v4l2_std_id) -#define VIDIOC_S_STD _IOW ('V', 24, v4l2_std_id) -#define VIDIOC_ENUMSTD _IOWR ('V', 25, struct v4l2_standard) -#define VIDIOC_ENUMINPUT _IOWR ('V', 26, struct v4l2_input) -#define VIDIOC_G_CTRL _IOWR ('V', 27, struct v4l2_control) -#define VIDIOC_S_CTRL _IOWR ('V', 28, struct v4l2_control) -#define VIDIOC_G_TUNER _IOWR ('V', 29, struct v4l2_tuner) -#define VIDIOC_S_TUNER _IOW ('V', 30, struct v4l2_tuner) -#define VIDIOC_G_AUDIO _IOR ('V', 33, struct v4l2_audio) -#define VIDIOC_S_AUDIO _IOW ('V', 34, struct v4l2_audio) -#define VIDIOC_QUERYCTRL _IOWR ('V', 36, struct v4l2_queryctrl) -#define VIDIOC_QUERYMENU _IOWR ('V', 37, struct v4l2_querymenu) -#define VIDIOC_G_INPUT _IOR ('V', 38, int) -#define VIDIOC_S_INPUT _IOWR ('V', 39, int) -#define VIDIOC_G_OUTPUT _IOR ('V', 46, int) -#define VIDIOC_S_OUTPUT _IOWR ('V', 47, int) -#define VIDIOC_ENUMOUTPUT _IOWR ('V', 48, struct v4l2_output) -#define VIDIOC_G_AUDOUT _IOR ('V', 49, struct v4l2_audioout) -#define VIDIOC_S_AUDOUT _IOW ('V', 50, struct v4l2_audioout) -#define VIDIOC_G_MODULATOR _IOWR ('V', 54, struct v4l2_modulator) -#define VIDIOC_S_MODULATOR _IOW ('V', 55, struct v4l2_modulator) -#define VIDIOC_G_FREQUENCY _IOWR ('V', 56, struct v4l2_frequency) -#define VIDIOC_S_FREQUENCY _IOW ('V', 57, struct v4l2_frequency) -#define VIDIOC_CROPCAP _IOWR ('V', 58, struct v4l2_cropcap) -#define VIDIOC_G_CROP _IOWR ('V', 59, struct v4l2_crop) -#define VIDIOC_S_CROP _IOW ('V', 60, struct v4l2_crop) -#define VIDIOC_G_JPEGCOMP _IOR ('V', 61, struct v4l2_jpegcompression) -#define VIDIOC_S_JPEGCOMP _IOW ('V', 62, struct v4l2_jpegcompression) -#define VIDIOC_QUERYSTD _IOR ('V', 63, v4l2_std_id) -#define VIDIOC_TRY_FMT _IOWR ('V', 64, struct v4l2_format) -#define VIDIOC_ENUMAUDIO _IOWR ('V', 65, struct v4l2_audio) -#define VIDIOC_ENUMAUDOUT _IOWR ('V', 66, struct v4l2_audioout) -#define VIDIOC_G_PRIORITY _IOR ('V', 67, enum v4l2_priority) -#define VIDIOC_S_PRIORITY _IOW ('V', 68, enum v4l2_priority) -#define VIDIOC_G_SLICED_VBI_CAP _IOWR ('V', 69, struct v4l2_sliced_vbi_cap) -#define VIDIOC_LOG_STATUS _IO ('V', 70) -#define VIDIOC_G_EXT_CTRLS _IOWR ('V', 71, struct v4l2_ext_controls) -#define VIDIOC_S_EXT_CTRLS _IOWR ('V', 72, struct v4l2_ext_controls) -#define VIDIOC_TRY_EXT_CTRLS _IOWR ('V', 73, struct v4l2_ext_controls) +#define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability) +#define VIDIOC_RESERVED _IO('V', 1) +#define VIDIOC_ENUM_FMT _IOWR('V', 2, struct v4l2_fmtdesc) +#define VIDIOC_G_FMT _IOWR('V', 4, struct v4l2_format) +#define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) +#define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers) +#define VIDIOC_QUERYBUF _IOWR('V', 9, struct v4l2_buffer) +#define VIDIOC_G_FBUF _IOR('V', 10, struct v4l2_framebuffer) +#define VIDIOC_S_FBUF _IOW('V', 11, struct v4l2_framebuffer) +#define VIDIOC_OVERLAY _IOW('V', 14, int) +#define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer) +#define VIDIOC_DQBUF _IOWR('V', 17, struct v4l2_buffer) +#define VIDIOC_STREAMON _IOW('V', 18, int) +#define VIDIOC_STREAMOFF _IOW('V', 19, int) +#define VIDIOC_G_PARM _IOWR('V', 21, struct v4l2_streamparm) +#define VIDIOC_S_PARM _IOWR('V', 22, struct v4l2_streamparm) +#define VIDIOC_G_STD _IOR('V', 23, v4l2_std_id) +#define VIDIOC_S_STD _IOW('V', 24, v4l2_std_id) +#define VIDIOC_ENUMSTD _IOWR('V', 25, struct v4l2_standard) +#define VIDIOC_ENUMINPUT _IOWR('V', 26, struct v4l2_input) +#define VIDIOC_G_CTRL _IOWR('V', 27, struct v4l2_control) +#define VIDIOC_S_CTRL _IOWR('V', 28, struct v4l2_control) +#define VIDIOC_G_TUNER _IOWR('V', 29, struct v4l2_tuner) +#define VIDIOC_S_TUNER _IOW('V', 30, struct v4l2_tuner) +#define VIDIOC_G_AUDIO _IOR('V', 33, struct v4l2_audio) +#define VIDIOC_S_AUDIO _IOW('V', 34, struct v4l2_audio) +#define VIDIOC_QUERYCTRL _IOWR('V', 36, struct v4l2_queryctrl) +#define VIDIOC_QUERYMENU _IOWR('V', 37, struct v4l2_querymenu) +#define VIDIOC_G_INPUT _IOR('V', 38, int) +#define VIDIOC_S_INPUT _IOWR('V', 39, int) +#define VIDIOC_G_OUTPUT _IOR('V', 46, int) +#define VIDIOC_S_OUTPUT _IOWR('V', 47, int) +#define VIDIOC_ENUMOUTPUT _IOWR('V', 48, struct v4l2_output) +#define VIDIOC_G_AUDOUT _IOR('V', 49, struct v4l2_audioout) +#define VIDIOC_S_AUDOUT _IOW('V', 50, struct v4l2_audioout) +#define VIDIOC_G_MODULATOR _IOWR('V', 54, struct v4l2_modulator) +#define VIDIOC_S_MODULATOR _IOW('V', 55, struct v4l2_modulator) +#define VIDIOC_G_FREQUENCY _IOWR('V', 56, struct v4l2_frequency) +#define VIDIOC_S_FREQUENCY _IOW('V', 57, struct v4l2_frequency) +#define VIDIOC_CROPCAP _IOWR('V', 58, struct v4l2_cropcap) +#define VIDIOC_G_CROP _IOWR('V', 59, struct v4l2_crop) +#define VIDIOC_S_CROP _IOW('V', 60, struct v4l2_crop) +#define VIDIOC_G_JPEGCOMP _IOR('V', 61, struct v4l2_jpegcompression) +#define VIDIOC_S_JPEGCOMP _IOW('V', 62, struct v4l2_jpegcompression) +#define VIDIOC_QUERYSTD _IOR('V', 63, v4l2_std_id) +#define VIDIOC_TRY_FMT _IOWR('V', 64, struct v4l2_format) +#define VIDIOC_ENUMAUDIO _IOWR('V', 65, struct v4l2_audio) +#define VIDIOC_ENUMAUDOUT _IOWR('V', 66, struct v4l2_audioout) +#define VIDIOC_G_PRIORITY _IOR('V', 67, enum v4l2_priority) +#define VIDIOC_S_PRIORITY _IOW('V', 68, enum v4l2_priority) +#define VIDIOC_G_SLICED_VBI_CAP _IOWR('V', 69, struct v4l2_sliced_vbi_cap) +#define VIDIOC_LOG_STATUS _IO('V', 70) +#define VIDIOC_G_EXT_CTRLS _IOWR('V', 71, struct v4l2_ext_controls) +#define VIDIOC_S_EXT_CTRLS _IOWR('V', 72, struct v4l2_ext_controls) +#define VIDIOC_TRY_EXT_CTRLS _IOWR('V', 73, struct v4l2_ext_controls) #if 1 -#define VIDIOC_ENUM_FRAMESIZES _IOWR ('V', 74, struct v4l2_frmsizeenum) -#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR ('V', 75, struct v4l2_frmivalenum) -#define VIDIOC_G_ENC_INDEX _IOR ('V', 76, struct v4l2_enc_idx) -#define VIDIOC_ENCODER_CMD _IOWR ('V', 77, struct v4l2_encoder_cmd) -#define VIDIOC_TRY_ENCODER_CMD _IOWR ('V', 78, struct v4l2_encoder_cmd) +#define VIDIOC_ENUM_FRAMESIZES _IOWR('V', 74, struct v4l2_frmsizeenum) +#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct v4l2_frmivalenum) +#define VIDIOC_G_ENC_INDEX _IOR('V', 76, struct v4l2_enc_idx) +#define VIDIOC_ENCODER_CMD _IOWR('V', 77, struct v4l2_encoder_cmd) +#define VIDIOC_TRY_ENCODER_CMD _IOWR('V', 78, struct v4l2_encoder_cmd) /* Experimental, only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */ -#define VIDIOC_DBG_S_REGISTER _IOW ('V', 79, struct v4l2_register) -#define VIDIOC_DBG_G_REGISTER _IOWR ('V', 80, struct v4l2_register) +#define VIDIOC_DBG_S_REGISTER _IOW('V', 79, struct v4l2_register) +#define VIDIOC_DBG_G_REGISTER _IOWR('V', 80, struct v4l2_register) -#define VIDIOC_G_CHIP_IDENT _IOWR ('V', 81, struct v4l2_chip_ident) +#define VIDIOC_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_chip_ident) #endif -#define VIDIOC_S_HW_FREQ_SEEK _IOW ('V', 82, struct v4l2_hw_freq_seek) +#define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek) #ifdef __OLD_VIDIOC_ /* for compatibility, will go away some day */ -#define VIDIOC_OVERLAY_OLD _IOWR ('V', 14, int) -#define VIDIOC_S_PARM_OLD _IOW ('V', 22, struct v4l2_streamparm) -#define VIDIOC_S_CTRL_OLD _IOW ('V', 28, struct v4l2_control) -#define VIDIOC_G_AUDIO_OLD _IOWR ('V', 33, struct v4l2_audio) -#define VIDIOC_G_AUDOUT_OLD _IOWR ('V', 49, struct v4l2_audioout) -#define VIDIOC_CROPCAP_OLD _IOR ('V', 58, struct v4l2_cropcap) +#define VIDIOC_OVERLAY_OLD _IOWR('V', 14, int) +#define VIDIOC_S_PARM_OLD _IOW('V', 22, struct v4l2_streamparm) +#define VIDIOC_S_CTRL_OLD _IOW('V', 28, struct v4l2_control) +#define VIDIOC_G_AUDIO_OLD _IOWR('V', 33, struct v4l2_audio) +#define VIDIOC_G_AUDOUT_OLD _IOWR('V', 49, struct v4l2_audioout) +#define VIDIOC_CROPCAP_OLD _IOR('V', 58, struct v4l2_cropcap) #endif #define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */ #endif /* __LINUX_VIDEODEV2_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit From 1250ac6d4ab716dafe0ac245fd31cd3a7cbc0a98 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 26 Jul 2008 08:02:47 -0300 Subject: V4L/DVB (8518): gspca: Remove the remaining frame decoding functions from the subdrivers. SPCA505 and SPCA508 added in the pixel formats. Decode functions and associated resources removed in spca505, 506 and 508. The decode routines are now found in the V4L library. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/spca505.c | 105 ++++++++++-------------------------- drivers/media/video/gspca/spca506.c | 105 ++++++++++-------------------------- drivers/media/video/gspca/spca508.c | 91 +++++++------------------------ include/linux/videodev2.h | 2 + 4 files changed, 76 insertions(+), 227 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index 284d549e4d3e..32ffe5556061 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -31,10 +31,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int buflen; - unsigned char tmpbuf[640 * 480 * 3 / 2]; /* YYUV per line */ - unsigned char tmpbuf2[640 * 480 * 2]; /* YUYV */ - unsigned char brightness; char subtype; @@ -64,29 +60,29 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 160 * 2, - .sizeimage = 160 * 120 * 2, + {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 160 * 3, + .sizeimage = 160 * 120 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 5}, - {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 176 * 2, - .sizeimage = 176 * 144 * 2, + {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 176 * 3, + .sizeimage = 176 * 144 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 4}, - {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, + {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 320 * 3, + .sizeimage = 320 * 240 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, - {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 352 * 2, - .sizeimage = 352 * 288 * 2, + {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 352 * 3, + .sizeimage = 352 * 288 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, - {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 640 * 2, - .sizeimage = 640 * 480 * 2, + {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 640 * 3, + .sizeimage = 640 * 480 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -760,77 +756,30 @@ static void sd_close(struct gspca_dev *gspca_dev) reg_write(gspca_dev->dev, 0x05, 0x11, 0xf); } -/* convert YYUV per line to YUYV (YUV 4:2:2) */ -static void yyuv_decode(unsigned char *out, - unsigned char *in, - int width, - int height) -{ - unsigned char *Ui, *Vi, *yi, *yi1; - unsigned char *out1; - int i, j; - - yi = in; - for (i = height / 2; --i >= 0; ) { - out1 = out + width * 2; /* next line */ - yi1 = yi + width; - Ui = yi1 + width; - Vi = Ui + width / 2; - for (j = width / 2; --j >= 0; ) { - *out++ = 128 + *yi++; - *out++ = 128 + *Ui; - *out++ = 128 + *yi++; - *out++ = 128 + *Vi; - - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Ui++; - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Vi++; - } - yi += width * 2; - out = out1; - } -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { - struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { case 0: /* start of frame */ - if (gspca_dev->last_packet_type == FIRST_PACKET) { - yyuv_decode(sd->tmpbuf2, sd->tmpbuf, - gspca_dev->width, - gspca_dev->height); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - sd->tmpbuf2, - gspca_dev->width - * gspca_dev->height - * 2); - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - if (len > 0) - memcpy(sd->tmpbuf, data, len); - else - len = 0; - sd->buflen = len; - return; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; } - data += 1; - len -= 1; - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index 2c281a0563e5..6fe715c80ad2 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -33,10 +33,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int buflen; - __u8 tmpbuf[640 * 480 * 3]; /* YYUV per line */ - __u8 tmpbuf2[640 * 480 * 2]; /* YUYV */ - unsigned char brightness; unsigned char contrast; unsigned char colors; @@ -115,29 +111,29 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 160 * 2, - .sizeimage = 160 * 120 * 2, + {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 160 * 3, + .sizeimage = 160 * 120 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 5}, - {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 176 * 2, - .sizeimage = 176 * 144 * 2, + {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 176 * 3, + .sizeimage = 176 * 144 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 4}, - {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, + {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 320 * 3, + .sizeimage = 320 * 240 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, - {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 352 * 2, - .sizeimage = 352 * 288 * 2, + {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 352 * 3, + .sizeimage = 352 * 288 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, - {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 640 * 2, - .sizeimage = 640 * 480 * 2, + {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, + .bytesperline = 640 * 3, + .sizeimage = 640 * 480 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -572,77 +568,30 @@ static void sd_close(struct gspca_dev *gspca_dev) { } -/* convert YYUV per line to YUYV (YUV 4:2:2) */ -static void yyuv_decode(unsigned char *out, - unsigned char *in, - int width, - int height) -{ - unsigned char *Ui, *Vi, *yi, *yi1; - unsigned char *out1; - int i, j; - - yi = in; - for (i = height / 2; --i >= 0; ) { - out1 = out + width * 2; /* next line */ - yi1 = yi + width; - Ui = yi1 + width; - Vi = Ui + width / 2; - for (j = width / 2; --j >= 0; ) { - *out++ = 128 + *yi++; - *out++ = 128 + *Ui; - *out++ = 128 + *yi++; - *out++ = 128 + *Vi; - - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Ui++; - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Vi++; - } - yi += width * 2; - out = out1; - } -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { - struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { case 0: /* start of frame */ - if (gspca_dev->last_packet_type == FIRST_PACKET) { - yyuv_decode(sd->tmpbuf2, sd->tmpbuf, - gspca_dev->width, - gspca_dev->height); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - sd->tmpbuf2, - gspca_dev->width - * gspca_dev->height - * 2); - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - if (len > 0) - memcpy(sd->tmpbuf, data, len); - else - len = 0; - sd->buflen = len; - return; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; } - data += 1; - len -= 1; - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index af531d62856c..4378e966edcc 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -30,10 +30,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int buflen; - unsigned char tmpbuf[352 * 288 * 3 / 2]; /* YUVY per line */ - unsigned char tmpbuf2[352 * 288 * 2]; /* YUYV */ - unsigned char brightness; char subtype; @@ -68,23 +64,23 @@ static struct ctrl sd_ctrls[] = { static struct v4l2_pix_format sif_mode[] = { {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 160 * 2, - .sizeimage = 160 * 120 * 2, + .bytesperline = 160 * 3, + .sizeimage = 160 * 120 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 3}, {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 176 * 2, - .sizeimage = 176 * 144 * 2, + .bytesperline = 176 * 3, + .sizeimage = 176 * 144 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320 * 2, - .sizeimage = 320 * 240 * 2, + .bytesperline = 320 * 3, + .sizeimage = 320 * 240 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 352 * 2, - .sizeimage = 352 * 288 * 2, + .bytesperline = 352 * 3, + .sizeimage = 352 * 288 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -1567,77 +1563,30 @@ static void sd_close(struct gspca_dev *gspca_dev) { } -/* convert YUVY per line to YUYV (YUV 4:2:2) */ -static void yuvy_decode(unsigned char *out, - unsigned char *in, - int width, - int height) -{ - unsigned char *Ui, *Vi, *yi, *yi1; - unsigned char *out1; - int i, j; - - yi = in; - for (i = height / 2; --i >= 0; ) { - out1 = out + width * 2; /* next line */ - Ui = yi + width; - Vi = Ui + width / 2; - yi1 = Vi + width / 2; - for (j = width / 2; --j >= 0; ) { - *out++ = 128 + *yi++; - *out++ = 128 + *Ui; - *out++ = 128 + *yi++; - *out++ = 128 + *Vi; - - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Ui++; - *out1++ = 128 + *yi1++; - *out1++ = 128 + *Vi++; - } - yi += width * 2; - out = out1; - } -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { - struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { case 0: /* start of frame */ - if (gspca_dev->last_packet_type == FIRST_PACKET) { - yuvy_decode(sd->tmpbuf2, sd->tmpbuf, - gspca_dev->width, - gspca_dev->height); - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - sd->tmpbuf2, - gspca_dev->width - * gspca_dev->height - * 2); - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, 0); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); data += SPCA508_OFFSET_DATA; len -= SPCA508_OFFSET_DATA; - if (len > 0) - memcpy(sd->tmpbuf, data, len); - else - len = 0; - sd->buflen = len; - return; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; + break; + default: + data += 1; + len -= 1; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + break; } - data += 1; - len -= 1; - memcpy(&sd->tmpbuf[sd->buflen], data, len); - sd->buflen += len; } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index cc0c8952323b..7d9ac046389e 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -324,6 +324,8 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */ #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */ #define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */ +#define V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S','5','0','5') /* YYUV per line */ +#define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S','5','0','8') /* YUVY per line */ #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ -- cgit From c1d7f4f1648cb8efd87f1b9560c40af2297e7c05 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 26 Jul 2008 08:33:47 -0300 Subject: V4L/DVB (8524): videodev: copy the VID_TYPE defines to videodev.h The VID_TYPE defines are V4L1 specific, so copy them back to videodev.h. In videodev2.h ensure that they are not used in the kernel (you need to include videodev.h instead) and mark them are deprecated. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev.h | 15 +++++++++++++++ include/linux/videodev2.h | 6 ++++++ 2 files changed, 21 insertions(+) (limited to 'include/linux') diff --git a/include/linux/videodev.h b/include/linux/videodev.h index 9385a566aed8..15a653d41132 100644 --- a/include/linux/videodev.h +++ b/include/linux/videodev.h @@ -17,6 +17,21 @@ #if defined(CONFIG_VIDEO_V4L1_COMPAT) || !defined (__KERNEL__) +#define VID_TYPE_CAPTURE 1 /* Can capture */ +#define VID_TYPE_TUNER 2 /* Can tune */ +#define VID_TYPE_TELETEXT 4 /* Does teletext */ +#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ +#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ +#define VID_TYPE_CLIPPING 32 /* Can clip */ +#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ +#define VID_TYPE_SCALES 128 /* Scalable */ +#define VID_TYPE_MONOCHROME 256 /* Monochrome only */ +#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ +#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ +#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ +#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ +#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ + struct video_capability { char name[32]; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 7d9ac046389e..f7195351a1e7 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -71,6 +71,11 @@ */ #define VIDEO_MAX_FRAME 32 +#ifndef __KERNEL__ + +/* These defines are V4L1 specific and should not be used with the V4L2 API! + They will be removed from this header in the future. */ + #define VID_TYPE_CAPTURE 1 /* Can capture */ #define VID_TYPE_TUNER 2 /* Can tune */ #define VID_TYPE_TELETEXT 4 /* Does teletext */ @@ -85,6 +90,7 @@ #define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ #define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ #define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ +#endif /* * M I S C E L L A N E O U S -- cgit From 9fa0f6db3a201bef49f28e69f80802559a38586b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 27 Jul 2008 08:55:17 -0300 Subject: V4L/DVB (8522): videodev2: Fix merge conflict Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index f7195351a1e7..e466bd54a50e 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -330,8 +330,8 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P', 'W', 'C', '2') /* pwc newer webcam */ #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E', '6', '2', '5') /* ET61X251 compression */ #define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */ -#define V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S','5','0','5') /* YYUV per line */ -#define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S','5','0','8') /* YUVY per line */ +#define V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S', '5', '0', '5') /* YYUV per line */ +#define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */ #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ -- cgit From 5995477ab7f3522c497c9c4a1c55373e9d655574 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Sun, 27 Jul 2008 17:29:15 +0200 Subject: task IO accounting: improve code readability Put all i/o statistics in struct proc_io_accounting and use inline functions to initialize and increment statistics, removing a lot of single variable assignments. This also reduces the kernel size as following (with CONFIG_TASK_XACCT=y and CONFIG_TASK_IO_ACCOUNTING=y). text data bss dec hex filename 11651 0 0 11651 2d83 kernel/exit.o.before 11619 0 0 11619 2d63 kernel/exit.o.after 10886 132 136 11154 2b92 kernel/fork.o.before 10758 132 136 11026 2b12 kernel/fork.o.after 3082029 807968 4818600 8708597 84e1f5 vmlinux.o.before 3081869 807968 4818600 8708437 84e155 vmlinux.o.after Signed-off-by: Andrea Righi Acked-by: Oleg Nesterov Signed-off-by: Linus Torvalds --- fs/proc/base.c | 57 ++++++++++------------------------ include/linux/sched.h | 19 ++++-------- include/linux/task_io_accounting.h | 27 ++++++++++++++-- include/linux/task_io_accounting_ops.h | 56 +++++++++++++++++++++++++++------ kernel/exit.c | 30 ++---------------- kernel/fork.c | 15 ++------- kernel/tsacct.c | 14 ++++----- 7 files changed, 104 insertions(+), 114 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/base.c b/fs/proc/base.c index e74308bdabd3..3d94906c7aa8 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -2402,44 +2403,17 @@ static int proc_base_fill_cache(struct file *filp, void *dirent, #ifdef CONFIG_TASK_IO_ACCOUNTING static int do_io_accounting(struct task_struct *task, char *buffer, int whole) { - u64 rchar, wchar, syscr, syscw; - struct task_io_accounting ioac; - - rchar = task->rchar; - wchar = task->wchar; - syscr = task->syscr; - syscw = task->syscw; - memcpy(&ioac, &task->ioac, sizeof(ioac)); - - if (whole) { - unsigned long flags; - - if (lock_task_sighand(task, &flags)) { - struct signal_struct *sig = task->signal; - struct task_struct *t = task; - - rchar += sig->rchar; - wchar += sig->wchar; - syscr += sig->syscr; - syscw += sig->syscw; - - ioac.read_bytes += sig->ioac.read_bytes; - ioac.write_bytes += sig->ioac.write_bytes; - ioac.cancelled_write_bytes += - sig->ioac.cancelled_write_bytes; - while_each_thread(task, t) { - rchar += t->rchar; - wchar += t->wchar; - syscr += t->syscr; - syscw += t->syscw; - - ioac.read_bytes += t->ioac.read_bytes; - ioac.write_bytes += t->ioac.write_bytes; - ioac.cancelled_write_bytes += - t->ioac.cancelled_write_bytes; - } - unlock_task_sighand(task, &flags); - } + struct proc_io_accounting acct = task->ioac; + unsigned long flags; + + if (whole && lock_task_sighand(task, &flags)) { + struct task_struct *t = task; + + task_io_accounting_add(&acct, &task->signal->ioac); + while_each_thread(task, t) + task_io_accounting_add(&acct, &t->ioac); + + unlock_task_sighand(task, &flags); } return sprintf(buffer, "rchar: %llu\n" @@ -2449,9 +2423,10 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole) "read_bytes: %llu\n" "write_bytes: %llu\n" "cancelled_write_bytes: %llu\n", - rchar, wchar, syscr, syscw, - ioac.read_bytes, ioac.write_bytes, - ioac.cancelled_write_bytes); + acct.chr.rchar, acct.chr.wchar, + acct.chr.syscr, acct.chr.syscw, + acct.blk.read_bytes, acct.blk.write_bytes, + acct.blk.cancelled_write_bytes); } static int proc_tid_io_accounting(struct task_struct *task, char *buffer) diff --git a/include/linux/sched.h b/include/linux/sched.h index f59318a0099b..034c1ca6b332 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -505,10 +505,7 @@ struct signal_struct { unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw; unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt; unsigned long inblock, oublock, cinblock, coublock; -#ifdef CONFIG_TASK_XACCT - u64 rchar, wchar, syscr, syscw; -#endif - struct task_io_accounting ioac; + struct proc_io_accounting ioac; /* * Cumulative ns of scheduled CPU time for dead threads in the @@ -1256,11 +1253,7 @@ struct task_struct { unsigned long ptrace_message; siginfo_t *last_siginfo; /* For ptrace use. */ -#ifdef CONFIG_TASK_XACCT -/* i/o counters(bytes read/written, #syscalls */ - u64 rchar, wchar, syscr, syscw; -#endif - struct task_io_accounting ioac; + struct proc_io_accounting ioac; #if defined(CONFIG_TASK_XACCT) u64 acct_rss_mem1; /* accumulated rss usage */ u64 acct_vm_mem1; /* accumulated virtual memory usage */ @@ -2190,22 +2183,22 @@ extern long sched_group_rt_period(struct task_group *tg); #ifdef CONFIG_TASK_XACCT static inline void add_rchar(struct task_struct *tsk, ssize_t amt) { - tsk->rchar += amt; + tsk->ioac.chr.rchar += amt; } static inline void add_wchar(struct task_struct *tsk, ssize_t amt) { - tsk->wchar += amt; + tsk->ioac.chr.wchar += amt; } static inline void inc_syscr(struct task_struct *tsk) { - tsk->syscr++; + tsk->ioac.chr.syscr++; } static inline void inc_syscw(struct task_struct *tsk) { - tsk->syscw++; + tsk->ioac.chr.syscw++; } #else static inline void add_rchar(struct task_struct *tsk, ssize_t amt) diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h index 44d00e9cceea..165390f8b936 100644 --- a/include/linux/task_io_accounting.h +++ b/include/linux/task_io_accounting.h @@ -1,5 +1,5 @@ /* - * task_io_accounting: a structure which is used for recording a single task's + * proc_io_accounting: a structure which is used for recording a single task's * IO statistics. * * Don't include this header file directly - it is designed to be dragged in via @@ -8,6 +8,22 @@ * Blame akpm@osdl.org for all this. */ +#ifdef CONFIG_TASK_XACCT +struct task_chr_io_accounting { + /* bytes read */ + u64 rchar; + /* bytes written */ + u64 wchar; + /* # of read syscalls */ + u64 syscr; + /* # of write syscalls */ + u64 syscw; +}; +#else /* CONFIG_TASK_XACCT */ +struct task_chr_io_accounting { +}; +#endif /* CONFIG_TASK_XACCT */ + #ifdef CONFIG_TASK_IO_ACCOUNTING struct task_io_accounting { /* @@ -31,7 +47,12 @@ struct task_io_accounting { */ u64 cancelled_write_bytes; }; -#else +#else /* CONFIG_TASK_IO_ACCOUNTING */ struct task_io_accounting { }; -#endif +#endif /* CONFIG_TASK_IO_ACCOUNTING */ + +struct proc_io_accounting { + struct task_chr_io_accounting chr; + struct task_io_accounting blk; +}; diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h index ff46c6fad79d..e6f958ebe97f 100644 --- a/include/linux/task_io_accounting_ops.h +++ b/include/linux/task_io_accounting_ops.h @@ -9,7 +9,7 @@ #ifdef CONFIG_TASK_IO_ACCOUNTING static inline void task_io_account_read(size_t bytes) { - current->ioac.read_bytes += bytes; + current->ioac.blk.read_bytes += bytes; } /* @@ -18,12 +18,12 @@ static inline void task_io_account_read(size_t bytes) */ static inline unsigned long task_io_get_inblock(const struct task_struct *p) { - return p->ioac.read_bytes >> 9; + return p->ioac.blk.read_bytes >> 9; } static inline void task_io_account_write(size_t bytes) { - current->ioac.write_bytes += bytes; + current->ioac.blk.write_bytes += bytes; } /* @@ -32,17 +32,25 @@ static inline void task_io_account_write(size_t bytes) */ static inline unsigned long task_io_get_oublock(const struct task_struct *p) { - return p->ioac.write_bytes >> 9; + return p->ioac.blk.write_bytes >> 9; } static inline void task_io_account_cancelled_write(size_t bytes) { - current->ioac.cancelled_write_bytes += bytes; + current->ioac.blk.cancelled_write_bytes += bytes; } -static inline void task_io_accounting_init(struct task_struct *tsk) +static inline void task_io_accounting_init(struct proc_io_accounting *ioac) { - memset(&tsk->ioac, 0, sizeof(tsk->ioac)); + memset(ioac, 0, sizeof(*ioac)); +} + +static inline void task_blk_io_accounting_add(struct proc_io_accounting *dst, + struct proc_io_accounting *src) +{ + dst->blk.read_bytes += src->blk.read_bytes; + dst->blk.write_bytes += src->blk.write_bytes; + dst->blk.cancelled_write_bytes += src->blk.cancelled_write_bytes; } #else @@ -69,9 +77,37 @@ static inline void task_io_account_cancelled_write(size_t bytes) { } -static inline void task_io_accounting_init(struct task_struct *tsk) +static inline void task_io_accounting_init(struct proc_io_accounting *ioac) +{ +} + +static inline void task_blk_io_accounting_add(struct proc_io_accounting *dst, + struct proc_io_accounting *src) { } -#endif /* CONFIG_TASK_IO_ACCOUNTING */ -#endif /* __TASK_IO_ACCOUNTING_OPS_INCLUDED */ +#endif /* CONFIG_TASK_IO_ACCOUNTING */ + +#ifdef CONFIG_TASK_XACCT +static inline void task_chr_io_accounting_add(struct proc_io_accounting *dst, + struct proc_io_accounting *src) +{ + dst->chr.rchar += src->chr.rchar; + dst->chr.wchar += src->chr.wchar; + dst->chr.syscr += src->chr.syscr; + dst->chr.syscw += src->chr.syscw; +} +#else +static inline void task_chr_io_accounting_add(struct proc_io_accounting *dst, + struct proc_io_accounting *src) +{ +} +#endif /* CONFIG_TASK_XACCT */ + +static inline void task_io_accounting_add(struct proc_io_accounting *dst, + struct proc_io_accounting *src) +{ + task_chr_io_accounting_add(dst, src); + task_blk_io_accounting_add(dst, src); +} +#endif /* __TASK_IO_ACCOUNTING_OPS_INCLUDED */ diff --git a/kernel/exit.c b/kernel/exit.c index 0caf590548a0..eb4d6470d1d0 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -121,18 +121,7 @@ static void __exit_signal(struct task_struct *tsk) sig->nivcsw += tsk->nivcsw; sig->inblock += task_io_get_inblock(tsk); sig->oublock += task_io_get_oublock(tsk); -#ifdef CONFIG_TASK_XACCT - sig->rchar += tsk->rchar; - sig->wchar += tsk->wchar; - sig->syscr += tsk->syscr; - sig->syscw += tsk->syscw; -#endif /* CONFIG_TASK_XACCT */ -#ifdef CONFIG_TASK_IO_ACCOUNTING - sig->ioac.read_bytes += tsk->ioac.read_bytes; - sig->ioac.write_bytes += tsk->ioac.write_bytes; - sig->ioac.cancelled_write_bytes += - tsk->ioac.cancelled_write_bytes; -#endif /* CONFIG_TASK_IO_ACCOUNTING */ + task_io_accounting_add(&sig->ioac, &tsk->ioac); sig->sum_sched_runtime += tsk->se.sum_exec_runtime; sig = NULL; /* Marker for below. */ } @@ -1363,21 +1352,8 @@ static int wait_task_zombie(struct task_struct *p, int options, psig->coublock += task_io_get_oublock(p) + sig->oublock + sig->coublock; -#ifdef CONFIG_TASK_XACCT - psig->rchar += p->rchar + sig->rchar; - psig->wchar += p->wchar + sig->wchar; - psig->syscr += p->syscr + sig->syscr; - psig->syscw += p->syscw + sig->syscw; -#endif /* CONFIG_TASK_XACCT */ -#ifdef CONFIG_TASK_IO_ACCOUNTING - psig->ioac.read_bytes += - p->ioac.read_bytes + sig->ioac.read_bytes; - psig->ioac.write_bytes += - p->ioac.write_bytes + sig->ioac.write_bytes; - psig->ioac.cancelled_write_bytes += - p->ioac.cancelled_write_bytes + - sig->ioac.cancelled_write_bytes; -#endif /* CONFIG_TASK_IO_ACCOUNTING */ + task_io_accounting_add(&psig->ioac, &p->ioac); + task_io_accounting_add(&psig->ioac, &sig->ioac); spin_unlock_irq(&p->parent->sighand->siglock); } diff --git a/kernel/fork.c b/kernel/fork.c index 5e050c1317c4..8214ba7c8bb1 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -806,12 +806,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0; sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0; sig->inblock = sig->oublock = sig->cinblock = sig->coublock = 0; -#ifdef CONFIG_TASK_XACCT - sig->rchar = sig->wchar = sig->syscr = sig->syscw = 0; -#endif -#ifdef CONFIG_TASK_IO_ACCOUNTING - memset(&sig->ioac, 0, sizeof(sig->ioac)); -#endif + task_io_accounting_init(&sig->ioac); sig->sum_sched_runtime = 0; INIT_LIST_HEAD(&sig->cpu_timers[0]); INIT_LIST_HEAD(&sig->cpu_timers[1]); @@ -994,13 +989,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->last_switch_timestamp = 0; #endif -#ifdef CONFIG_TASK_XACCT - p->rchar = 0; /* I/O counter: bytes read */ - p->wchar = 0; /* I/O counter: bytes written */ - p->syscr = 0; /* I/O counter: read syscalls */ - p->syscw = 0; /* I/O counter: write syscalls */ -#endif - task_io_accounting_init(p); + task_io_accounting_init(&p->ioac); acct_clear_integrals(p); p->it_virt_expires = cputime_zero; diff --git a/kernel/tsacct.c b/kernel/tsacct.c index 3da47ccdc5e5..f9cd2561689c 100644 --- a/kernel/tsacct.c +++ b/kernel/tsacct.c @@ -94,14 +94,14 @@ void xacct_add_tsk(struct taskstats *stats, struct task_struct *p) stats->hiwater_vm = mm->hiwater_vm * PAGE_SIZE / KB; mmput(mm); } - stats->read_char = p->rchar; - stats->write_char = p->wchar; - stats->read_syscalls = p->syscr; - stats->write_syscalls = p->syscw; + stats->read_char = p->ioac.chr.rchar; + stats->write_char = p->ioac.chr.wchar; + stats->read_syscalls = p->ioac.chr.syscr; + stats->write_syscalls = p->ioac.chr.syscw; #ifdef CONFIG_TASK_IO_ACCOUNTING - stats->read_bytes = p->ioac.read_bytes; - stats->write_bytes = p->ioac.write_bytes; - stats->cancelled_write_bytes = p->ioac.cancelled_write_bytes; + stats->read_bytes = p->ioac.blk.read_bytes; + stats->write_bytes = p->ioac.blk.write_bytes; + stats->cancelled_write_bytes = p->ioac.blk.cancelled_write_bytes; #else stats->read_bytes = 0; stats->write_bytes = 0; -- cgit From 940389b8afad6495211614c13eb91ef7001773ec Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Mon, 28 Jul 2008 00:48:12 +0200 Subject: task IO accounting: move all IO statistics in struct task_io_accounting Simplify the code of include/linux/task_io_accounting.h. It is also more reasonable to have all the task i/o-related statistics in a single struct (task_io_accounting). Signed-off-by: Andrea Righi Signed-off-by: Oleg Nesterov Signed-off-by: Linus Torvalds --- fs/proc/base.c | 10 +++---- include/linux/sched.h | 12 ++++----- include/linux/task_io_accounting.h | 17 ++---------- include/linux/task_io_accounting_ops.h | 48 +++++++++++++++++----------------- kernel/tsacct.c | 14 +++++----- 5 files changed, 44 insertions(+), 57 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/base.c b/fs/proc/base.c index 3d94906c7aa8..01ed610f9b87 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2403,7 +2403,7 @@ static int proc_base_fill_cache(struct file *filp, void *dirent, #ifdef CONFIG_TASK_IO_ACCOUNTING static int do_io_accounting(struct task_struct *task, char *buffer, int whole) { - struct proc_io_accounting acct = task->ioac; + struct task_io_accounting acct = task->ioac; unsigned long flags; if (whole && lock_task_sighand(task, &flags)) { @@ -2423,10 +2423,10 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole) "read_bytes: %llu\n" "write_bytes: %llu\n" "cancelled_write_bytes: %llu\n", - acct.chr.rchar, acct.chr.wchar, - acct.chr.syscr, acct.chr.syscw, - acct.blk.read_bytes, acct.blk.write_bytes, - acct.blk.cancelled_write_bytes); + acct.rchar, acct.wchar, + acct.syscr, acct.syscw, + acct.read_bytes, acct.write_bytes, + acct.cancelled_write_bytes); } static int proc_tid_io_accounting(struct task_struct *task, char *buffer) diff --git a/include/linux/sched.h b/include/linux/sched.h index 034c1ca6b332..5270d449ff9d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -505,7 +505,7 @@ struct signal_struct { unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw; unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt; unsigned long inblock, oublock, cinblock, coublock; - struct proc_io_accounting ioac; + struct task_io_accounting ioac; /* * Cumulative ns of scheduled CPU time for dead threads in the @@ -1253,7 +1253,7 @@ struct task_struct { unsigned long ptrace_message; siginfo_t *last_siginfo; /* For ptrace use. */ - struct proc_io_accounting ioac; + struct task_io_accounting ioac; #if defined(CONFIG_TASK_XACCT) u64 acct_rss_mem1; /* accumulated rss usage */ u64 acct_vm_mem1; /* accumulated virtual memory usage */ @@ -2183,22 +2183,22 @@ extern long sched_group_rt_period(struct task_group *tg); #ifdef CONFIG_TASK_XACCT static inline void add_rchar(struct task_struct *tsk, ssize_t amt) { - tsk->ioac.chr.rchar += amt; + tsk->ioac.rchar += amt; } static inline void add_wchar(struct task_struct *tsk, ssize_t amt) { - tsk->ioac.chr.wchar += amt; + tsk->ioac.wchar += amt; } static inline void inc_syscr(struct task_struct *tsk) { - tsk->ioac.chr.syscr++; + tsk->ioac.syscr++; } static inline void inc_syscw(struct task_struct *tsk) { - tsk->ioac.chr.syscw++; + tsk->ioac.syscw++; } #else static inline void add_rchar(struct task_struct *tsk, ssize_t amt) diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h index 165390f8b936..5e88afc9a2fb 100644 --- a/include/linux/task_io_accounting.h +++ b/include/linux/task_io_accounting.h @@ -1,5 +1,5 @@ /* - * proc_io_accounting: a structure which is used for recording a single task's + * task_io_accounting: a structure which is used for recording a single task's * IO statistics. * * Don't include this header file directly - it is designed to be dragged in via @@ -8,8 +8,8 @@ * Blame akpm@osdl.org for all this. */ +struct task_io_accounting { #ifdef CONFIG_TASK_XACCT -struct task_chr_io_accounting { /* bytes read */ u64 rchar; /* bytes written */ @@ -18,14 +18,9 @@ struct task_chr_io_accounting { u64 syscr; /* # of write syscalls */ u64 syscw; -}; -#else /* CONFIG_TASK_XACCT */ -struct task_chr_io_accounting { -}; #endif /* CONFIG_TASK_XACCT */ #ifdef CONFIG_TASK_IO_ACCOUNTING -struct task_io_accounting { /* * The number of bytes which this task has caused to be read from * storage. @@ -46,13 +41,5 @@ struct task_io_accounting { * information loss in doing that. */ u64 cancelled_write_bytes; -}; -#else /* CONFIG_TASK_IO_ACCOUNTING */ -struct task_io_accounting { -}; #endif /* CONFIG_TASK_IO_ACCOUNTING */ - -struct proc_io_accounting { - struct task_chr_io_accounting chr; - struct task_io_accounting blk; }; diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h index e6f958ebe97f..4d090f9ee608 100644 --- a/include/linux/task_io_accounting_ops.h +++ b/include/linux/task_io_accounting_ops.h @@ -9,7 +9,7 @@ #ifdef CONFIG_TASK_IO_ACCOUNTING static inline void task_io_account_read(size_t bytes) { - current->ioac.blk.read_bytes += bytes; + current->ioac.read_bytes += bytes; } /* @@ -18,12 +18,12 @@ static inline void task_io_account_read(size_t bytes) */ static inline unsigned long task_io_get_inblock(const struct task_struct *p) { - return p->ioac.blk.read_bytes >> 9; + return p->ioac.read_bytes >> 9; } static inline void task_io_account_write(size_t bytes) { - current->ioac.blk.write_bytes += bytes; + current->ioac.write_bytes += bytes; } /* @@ -32,25 +32,25 @@ static inline void task_io_account_write(size_t bytes) */ static inline unsigned long task_io_get_oublock(const struct task_struct *p) { - return p->ioac.blk.write_bytes >> 9; + return p->ioac.write_bytes >> 9; } static inline void task_io_account_cancelled_write(size_t bytes) { - current->ioac.blk.cancelled_write_bytes += bytes; + current->ioac.cancelled_write_bytes += bytes; } -static inline void task_io_accounting_init(struct proc_io_accounting *ioac) +static inline void task_io_accounting_init(struct task_io_accounting *ioac) { memset(ioac, 0, sizeof(*ioac)); } -static inline void task_blk_io_accounting_add(struct proc_io_accounting *dst, - struct proc_io_accounting *src) +static inline void task_blk_io_accounting_add(struct task_io_accounting *dst, + struct task_io_accounting *src) { - dst->blk.read_bytes += src->blk.read_bytes; - dst->blk.write_bytes += src->blk.write_bytes; - dst->blk.cancelled_write_bytes += src->blk.cancelled_write_bytes; + dst->read_bytes += src->read_bytes; + dst->write_bytes += src->write_bytes; + dst->cancelled_write_bytes += src->cancelled_write_bytes; } #else @@ -77,35 +77,35 @@ static inline void task_io_account_cancelled_write(size_t bytes) { } -static inline void task_io_accounting_init(struct proc_io_accounting *ioac) +static inline void task_io_accounting_init(struct task_io_accounting *ioac) { } -static inline void task_blk_io_accounting_add(struct proc_io_accounting *dst, - struct proc_io_accounting *src) +static inline void task_blk_io_accounting_add(struct task_io_accounting *dst, + struct task_io_accounting *src) { } #endif /* CONFIG_TASK_IO_ACCOUNTING */ #ifdef CONFIG_TASK_XACCT -static inline void task_chr_io_accounting_add(struct proc_io_accounting *dst, - struct proc_io_accounting *src) +static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, + struct task_io_accounting *src) { - dst->chr.rchar += src->chr.rchar; - dst->chr.wchar += src->chr.wchar; - dst->chr.syscr += src->chr.syscr; - dst->chr.syscw += src->chr.syscw; + dst->rchar += src->rchar; + dst->wchar += src->wchar; + dst->syscr += src->syscr; + dst->syscw += src->syscw; } #else -static inline void task_chr_io_accounting_add(struct proc_io_accounting *dst, - struct proc_io_accounting *src) +static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, + struct task_io_accounting *src) { } #endif /* CONFIG_TASK_XACCT */ -static inline void task_io_accounting_add(struct proc_io_accounting *dst, - struct proc_io_accounting *src) +static inline void task_io_accounting_add(struct task_io_accounting *dst, + struct task_io_accounting *src) { task_chr_io_accounting_add(dst, src); task_blk_io_accounting_add(dst, src); diff --git a/kernel/tsacct.c b/kernel/tsacct.c index f9cd2561689c..8ebcd8532dfb 100644 --- a/kernel/tsacct.c +++ b/kernel/tsacct.c @@ -94,14 +94,14 @@ void xacct_add_tsk(struct taskstats *stats, struct task_struct *p) stats->hiwater_vm = mm->hiwater_vm * PAGE_SIZE / KB; mmput(mm); } - stats->read_char = p->ioac.chr.rchar; - stats->write_char = p->ioac.chr.wchar; - stats->read_syscalls = p->ioac.chr.syscr; - stats->write_syscalls = p->ioac.chr.syscw; + stats->read_char = p->ioac.rchar; + stats->write_char = p->ioac.wchar; + stats->read_syscalls = p->ioac.syscr; + stats->write_syscalls = p->ioac.syscw; #ifdef CONFIG_TASK_IO_ACCOUNTING - stats->read_bytes = p->ioac.blk.read_bytes; - stats->write_bytes = p->ioac.blk.write_bytes; - stats->cancelled_write_bytes = p->ioac.blk.cancelled_write_bytes; + stats->read_bytes = p->ioac.read_bytes; + stats->write_bytes = p->ioac.write_bytes; + stats->cancelled_write_bytes = p->ioac.cancelled_write_bytes; #else stats->read_bytes = 0; stats->write_bytes = 0; -- cgit From 5c2aed622571ac7c3c6ec182d6d3c318e4b45c8b Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Thu, 28 Feb 2008 11:33:03 -0500 Subject: stop_machine: add ALL_CPUS option -allow stop_mahcine_run() to call a function on all cpus. Calling stop_machine_run() with a 'ALL_CPUS' invokes this new behavior. stop_machine_run() proceeds as normal until the calling cpu has invoked 'fn'. Then, we tell all the other cpus to call 'fn'. Signed-off-by: Jason Baron Signed-off-by: Mathieu Desnoyers Signed-off-by: Rusty Russell CC: Adrian Bunk CC: Andi Kleen CC: Alexey Dobriyan CC: Christoph Hellwig CC: mingo@elte.hu CC: akpm@osdl.org --- include/linux/stop_machine.h | 8 +++++++- kernel/stop_machine.c | 32 +++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index 5bfc553bdb21..18af011c13af 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -8,11 +8,17 @@ #include #if defined(CONFIG_STOP_MACHINE) && defined(CONFIG_SMP) + +#define ALL_CPUS ~0U + /** * stop_machine_run: freeze the machine on all CPUs and run this function * @fn: the function to run * @data: the data ptr for the @fn() - * @cpu: the cpu to run @fn() on (or any, if @cpu == NR_CPUS. + * @cpu: if @cpu == n, run @fn() on cpu n + * if @cpu == NR_CPUS, run @fn() on any cpu + * if @cpu == ALL_CPUS, run @fn() first on the calling cpu, and then + * concurrently on all the other cpus * * Description: This causes a thread to be scheduled on every other cpu, * each of which disables interrupts, and finally interrupts are disabled diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index 738b411ff2d3..a473bd0cb71b 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -22,9 +22,17 @@ enum stopmachine_state { STOPMACHINE_WAIT, STOPMACHINE_PREPARE, STOPMACHINE_DISABLE_IRQ, + STOPMACHINE_RUN, STOPMACHINE_EXIT, }; +struct stop_machine_data { + int (*fn)(void *); + void *data; + struct completion done; + int run_all; +} smdata; + static enum stopmachine_state stopmachine_state; static unsigned int stopmachine_num_threads; static atomic_t stopmachine_thread_ack; @@ -33,6 +41,7 @@ static int stopmachine(void *cpu) { int irqs_disabled = 0; int prepared = 0; + int ran = 0; cpumask_of_cpu_ptr(cpumask, (int)(long)cpu); set_cpus_allowed_ptr(current, cpumask); @@ -58,6 +67,11 @@ static int stopmachine(void *cpu) prepared = 1; smp_mb(); /* Must read state first. */ atomic_inc(&stopmachine_thread_ack); + } else if (stopmachine_state == STOPMACHINE_RUN && !ran) { + smdata.fn(smdata.data); + ran = 1; + smp_mb(); /* Must read state first. */ + atomic_inc(&stopmachine_thread_ack); } /* Yield in first stage: migration threads need to * help our sisters onto their CPUs. */ @@ -136,11 +150,10 @@ static void restart_machine(void) preempt_enable_no_resched(); } -struct stop_machine_data { - int (*fn)(void *); - void *data; - struct completion done; -}; +static void run_other_cpus(void) +{ + stopmachine_set_state(STOPMACHINE_RUN); +} static int do_stop(void *_smdata) { @@ -150,6 +163,8 @@ static int do_stop(void *_smdata) ret = stop_machine(); if (ret == 0) { ret = smdata->fn(smdata->data); + if (smdata->run_all) + run_other_cpus(); restart_machine(); } @@ -173,14 +188,17 @@ struct task_struct *__stop_machine_run(int (*fn)(void *), void *data, struct stop_machine_data smdata; struct task_struct *p; + mutex_lock(&stopmachine_mutex); + smdata.fn = fn; smdata.data = data; + smdata.run_all = (cpu == ALL_CPUS) ? 1 : 0; init_completion(&smdata.done); - mutex_lock(&stopmachine_mutex); + smp_wmb(); /* make sure other cpus see smdata updates */ /* If they don't care which CPU fn runs on, bind to any online one. */ - if (cpu == NR_CPUS) + if (cpu == NR_CPUS || cpu == ALL_CPUS) cpu = raw_smp_processor_id(); p = kthread_create(do_stop, &smdata, "kstopmachine"); -- cgit From ffdb5976c47609c862917d4c186ecbb5706d2dda Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 28 Jul 2008 12:16:28 -0500 Subject: Simplify stop_machine stop_machine creates a kthread which creates kernel threads. We can create those threads directly and simplify things a little. Some care must be taken with CPU hotunplug, which has special needs, but that code seems more robust than it was in the past. Signed-off-by: Rusty Russell Acked-by: Christian Borntraeger --- include/linux/stop_machine.h | 20 ++- kernel/cpu.c | 13 +- kernel/stop_machine.c | 293 ++++++++++++++++++------------------------- 3 files changed, 136 insertions(+), 190 deletions(-) (limited to 'include/linux') diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index 18af011c13af..36c2c7284eb3 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -17,13 +17,12 @@ * @data: the data ptr for the @fn() * @cpu: if @cpu == n, run @fn() on cpu n * if @cpu == NR_CPUS, run @fn() on any cpu - * if @cpu == ALL_CPUS, run @fn() first on the calling cpu, and then - * concurrently on all the other cpus + * if @cpu == ALL_CPUS, run @fn() on every online CPU. * - * Description: This causes a thread to be scheduled on every other cpu, - * each of which disables interrupts, and finally interrupts are disabled - * on the current CPU. The result is that noone is holding a spinlock - * or inside any other preempt-disabled region when @fn() runs. + * Description: This causes a thread to be scheduled on every cpu, + * each of which disables interrupts. The result is that noone is + * holding a spinlock or inside any other preempt-disabled region when + * @fn() runs. * * This can be thought of as a very heavy write lock, equivalent to * grabbing every spinlock in the kernel. */ @@ -35,13 +34,10 @@ int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu); * @data: the data ptr for the @fn * @cpu: the cpu to run @fn on (or any, if @cpu == NR_CPUS. * - * Description: This is a special version of the above, which returns the - * thread which has run @fn(): kthread_stop will return the return value - * of @fn(). Used by hotplug cpu. + * Description: This is a special version of the above, which assumes cpus + * won't come or go while it's being called. Used by hotplug cpu. */ -struct task_struct *__stop_machine_run(int (*fn)(void *), void *data, - unsigned int cpu); - +int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu); #else static inline int stop_machine_run(int (*fn)(void *), void *data, diff --git a/kernel/cpu.c b/kernel/cpu.c index 10ba5f1004a5..cf79bb911371 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -216,7 +216,6 @@ static int __ref take_cpu_down(void *_param) static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) { int err, nr_calls = 0; - struct task_struct *p; cpumask_t old_allowed, tmp; void *hcpu = (void *)(long)cpu; unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0; @@ -250,19 +249,15 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) cpu_clear(cpu, tmp); set_cpus_allowed_ptr(current, &tmp); - p = __stop_machine_run(take_cpu_down, &tcd_param, cpu); + err = __stop_machine_run(take_cpu_down, &tcd_param, cpu); - if (IS_ERR(p) || cpu_online(cpu)) { + if (err || cpu_online(cpu)) { /* CPU didn't die: tell everyone. Can't complain. */ if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, hcpu) == NOTIFY_BAD) BUG(); - if (IS_ERR(p)) { - err = PTR_ERR(p); - goto out_allowed; - } - goto out_thread; + goto out_allowed; } /* Wait for it to sleep (leaving idle task). */ @@ -279,8 +274,6 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) check_for_tasks(cpu); -out_thread: - err = kthread_stop(p); out_allowed: set_cpus_allowed_ptr(current, &old_allowed); out_release: diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index a473bd0cb71b..35882dccc943 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -1,4 +1,4 @@ -/* Copyright 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation. +/* Copyright 2008, 2005 Rusty Russell rusty@rustcorp.com.au IBM Corporation. * GPL v2 and any later version. */ #include @@ -13,220 +13,177 @@ #include #include -/* Since we effect priority and affinity (both of which are visible - * to, and settable by outside processes) we do indirection via a - * kthread. */ - -/* Thread to stop each CPU in user context. */ +/* This controls the threads on each CPU. */ enum stopmachine_state { - STOPMACHINE_WAIT, + /* Dummy starting state for thread. */ + STOPMACHINE_NONE, + /* Awaiting everyone to be scheduled. */ STOPMACHINE_PREPARE, + /* Disable interrupts. */ STOPMACHINE_DISABLE_IRQ, + /* Run the function */ STOPMACHINE_RUN, + /* Exit */ STOPMACHINE_EXIT, }; +static enum stopmachine_state state; struct stop_machine_data { int (*fn)(void *); void *data; - struct completion done; - int run_all; -} smdata; + int fnret; +}; -static enum stopmachine_state stopmachine_state; -static unsigned int stopmachine_num_threads; -static atomic_t stopmachine_thread_ack; +/* Like num_online_cpus(), but hotplug cpu uses us, so we need this. */ +static unsigned int num_threads; +static atomic_t thread_ack; +static struct completion finished; +static DEFINE_MUTEX(lock); -static int stopmachine(void *cpu) +static void set_state(enum stopmachine_state newstate) { - int irqs_disabled = 0; - int prepared = 0; - int ran = 0; - cpumask_of_cpu_ptr(cpumask, (int)(long)cpu); - - set_cpus_allowed_ptr(current, cpumask); - - /* Ack: we are alive */ - smp_mb(); /* Theoretically the ack = 0 might not be on this CPU yet. */ - atomic_inc(&stopmachine_thread_ack); - - /* Simple state machine */ - while (stopmachine_state != STOPMACHINE_EXIT) { - if (stopmachine_state == STOPMACHINE_DISABLE_IRQ - && !irqs_disabled) { - local_irq_disable(); - hard_irq_disable(); - irqs_disabled = 1; - /* Ack: irqs disabled. */ - smp_mb(); /* Must read state first. */ - atomic_inc(&stopmachine_thread_ack); - } else if (stopmachine_state == STOPMACHINE_PREPARE - && !prepared) { - /* Everyone is in place, hold CPU. */ - preempt_disable(); - prepared = 1; - smp_mb(); /* Must read state first. */ - atomic_inc(&stopmachine_thread_ack); - } else if (stopmachine_state == STOPMACHINE_RUN && !ran) { - smdata.fn(smdata.data); - ran = 1; - smp_mb(); /* Must read state first. */ - atomic_inc(&stopmachine_thread_ack); - } - /* Yield in first stage: migration threads need to - * help our sisters onto their CPUs. */ - if (!prepared && !irqs_disabled) - yield(); - cpu_relax(); - } - - /* Ack: we are exiting. */ - smp_mb(); /* Must read state first. */ - atomic_inc(&stopmachine_thread_ack); - - if (irqs_disabled) - local_irq_enable(); - if (prepared) - preempt_enable(); - - return 0; + /* Reset ack counter. */ + atomic_set(&thread_ack, num_threads); + smp_wmb(); + state = newstate; } -/* Change the thread state */ -static void stopmachine_set_state(enum stopmachine_state state) +/* Last one to ack a state moves to the next state. */ +static void ack_state(void) { - atomic_set(&stopmachine_thread_ack, 0); - smp_wmb(); - stopmachine_state = state; - while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads) - cpu_relax(); + if (atomic_dec_and_test(&thread_ack)) { + /* If we're the last one to ack the EXIT, we're finished. */ + if (state == STOPMACHINE_EXIT) + complete(&finished); + else + set_state(state + 1); + } } -static int stop_machine(void) +/* This is the actual thread which stops the CPU. It exits by itself rather + * than waiting for kthread_stop(), because it's easier for hotplug CPU. */ +static int stop_cpu(struct stop_machine_data *smdata) { - int i, ret = 0; - - atomic_set(&stopmachine_thread_ack, 0); - stopmachine_num_threads = 0; - stopmachine_state = STOPMACHINE_WAIT; + enum stopmachine_state curstate = STOPMACHINE_NONE; + int uninitialized_var(ret); - for_each_online_cpu(i) { - if (i == raw_smp_processor_id()) - continue; - ret = kernel_thread(stopmachine, (void *)(long)i,CLONE_KERNEL); - if (ret < 0) - break; - stopmachine_num_threads++; - } - - /* Wait for them all to come to life. */ - while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads) { - yield(); + /* Simple state machine */ + do { + /* Chill out and ensure we re-read stopmachine_state. */ cpu_relax(); - } - - /* If some failed, kill them all. */ - if (ret < 0) { - stopmachine_set_state(STOPMACHINE_EXIT); - return ret; - } - - /* Now they are all started, make them hold the CPUs, ready. */ - preempt_disable(); - stopmachine_set_state(STOPMACHINE_PREPARE); - - /* Make them disable irqs. */ - local_irq_disable(); - hard_irq_disable(); - stopmachine_set_state(STOPMACHINE_DISABLE_IRQ); - - return 0; -} + if (state != curstate) { + curstate = state; + switch (curstate) { + case STOPMACHINE_DISABLE_IRQ: + local_irq_disable(); + hard_irq_disable(); + break; + case STOPMACHINE_RUN: + /* |= allows error detection if functions on + * multiple CPUs. */ + smdata->fnret |= smdata->fn(smdata->data); + break; + default: + break; + } + ack_state(); + } + } while (curstate != STOPMACHINE_EXIT); -static void restart_machine(void) -{ - stopmachine_set_state(STOPMACHINE_EXIT); local_irq_enable(); - preempt_enable_no_resched(); + do_exit(0); } -static void run_other_cpus(void) +/* Callback for CPUs which aren't supposed to do anything. */ +static int chill(void *unused) { - stopmachine_set_state(STOPMACHINE_RUN); + return 0; } -static int do_stop(void *_smdata) +int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) { - struct stop_machine_data *smdata = _smdata; - int ret; + int i, err; + struct stop_machine_data active, idle; + struct task_struct **threads; + + active.fn = fn; + active.data = data; + active.fnret = 0; + idle.fn = chill; + idle.data = NULL; + + /* If they don't care which cpu fn runs on, just pick one. */ + if (cpu == NR_CPUS) + cpu = any_online_cpu(cpu_online_map); + + /* This could be too big for stack on large machines. */ + threads = kcalloc(NR_CPUS, sizeof(threads[0]), GFP_KERNEL); + if (!threads) + return -ENOMEM; + + /* Set up initial state. */ + mutex_lock(&lock); + init_completion(&finished); + num_threads = num_online_cpus(); + set_state(STOPMACHINE_PREPARE); - ret = stop_machine(); - if (ret == 0) { - ret = smdata->fn(smdata->data); - if (smdata->run_all) - run_other_cpus(); - restart_machine(); - } + for_each_online_cpu(i) { + struct stop_machine_data *smdata; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - /* We're done: you can kthread_stop us now */ - complete(&smdata->done); + if (cpu == ALL_CPUS || i == cpu) + smdata = &active; + else + smdata = &idle; + + threads[i] = kthread_create((void *)stop_cpu, smdata, "kstop%u", + i); + if (IS_ERR(threads[i])) { + err = PTR_ERR(threads[i]); + threads[i] = NULL; + goto kill_threads; + } - /* Wait for kthread_stop */ - set_current_state(TASK_INTERRUPTIBLE); - while (!kthread_should_stop()) { - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - __set_current_state(TASK_RUNNING); - return ret; -} + /* Place it onto correct cpu. */ + kthread_bind(threads[i], i); -struct task_struct *__stop_machine_run(int (*fn)(void *), void *data, - unsigned int cpu) -{ - static DEFINE_MUTEX(stopmachine_mutex); - struct stop_machine_data smdata; - struct task_struct *p; + /* Make it highest prio. */ + if (sched_setscheduler_nocheck(threads[i], SCHED_FIFO, ¶m)) + BUG(); + } - mutex_lock(&stopmachine_mutex); + /* We've created all the threads. Wake them all: hold this CPU so one + * doesn't hit this CPU until we're ready. */ + cpu = get_cpu(); + for_each_online_cpu(i) + wake_up_process(threads[i]); - smdata.fn = fn; - smdata.data = data; - smdata.run_all = (cpu == ALL_CPUS) ? 1 : 0; - init_completion(&smdata.done); + /* This will release the thread on our CPU. */ + put_cpu(); + wait_for_completion(&finished); + mutex_unlock(&lock); - smp_wmb(); /* make sure other cpus see smdata updates */ + kfree(threads); - /* If they don't care which CPU fn runs on, bind to any online one. */ - if (cpu == NR_CPUS || cpu == ALL_CPUS) - cpu = raw_smp_processor_id(); + return active.fnret; - p = kthread_create(do_stop, &smdata, "kstopmachine"); - if (!IS_ERR(p)) { - struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; +kill_threads: + for_each_online_cpu(i) + if (threads[i]) + kthread_stop(threads[i]); + mutex_unlock(&lock); - /* One high-prio thread per cpu. We'll do this one. */ - sched_setscheduler_nocheck(p, SCHED_FIFO, ¶m); - kthread_bind(p, cpu); - wake_up_process(p); - wait_for_completion(&smdata.done); - } - mutex_unlock(&stopmachine_mutex); - return p; + kfree(threads); + return err; } int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) { - struct task_struct *p; int ret; /* No CPUs can come up or down during this. */ get_online_cpus(); - p = __stop_machine_run(fn, data, cpu); - if (!IS_ERR(p)) - ret = kthread_stop(p); - else - ret = PTR_ERR(p); + ret = __stop_machine_run(fn, data, cpu); put_online_cpus(); return ret; -- cgit From eeec4fad963490821348a331cca6102ae1c4a7a3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 28 Jul 2008 12:16:30 -0500 Subject: stop_machine(): stop_machine_run() changed to use cpu mask Instead of a "cpu" arg with magic values NR_CPUS (any cpu) and ~0 (all cpus), pass a cpumask_t. Allow NULL for the common case (where we don't care which CPU the function is run on): temporary cpumask_t's are usually considered bad for stack space. This deprecates stop_machine_run, to be removed soon when all the callers are dead. Signed-off-by: Rusty Russell --- include/linux/stop_machine.h | 34 ++++++++++++++++++++++++---------- kernel/cpu.c | 3 ++- kernel/stop_machine.c | 27 +++++++++++++-------------- 3 files changed, 39 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index 36c2c7284eb3..f1cb0ba6d715 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -5,19 +5,19 @@ (and more). So the "read" side to such a lock is anything which diables preeempt. */ #include +#include #include #if defined(CONFIG_STOP_MACHINE) && defined(CONFIG_SMP) +/* Deprecated, but useful for transition. */ #define ALL_CPUS ~0U /** - * stop_machine_run: freeze the machine on all CPUs and run this function + * stop_machine: freeze the machine on all CPUs and run this function * @fn: the function to run * @data: the data ptr for the @fn() - * @cpu: if @cpu == n, run @fn() on cpu n - * if @cpu == NR_CPUS, run @fn() on any cpu - * if @cpu == ALL_CPUS, run @fn() on every online CPU. + * @cpus: the cpus to run the @fn() on (NULL = any online cpu) * * Description: This causes a thread to be scheduled on every cpu, * each of which disables interrupts. The result is that noone is @@ -26,22 +26,22 @@ * * This can be thought of as a very heavy write lock, equivalent to * grabbing every spinlock in the kernel. */ -int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu); +int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus); /** - * __stop_machine_run: freeze the machine on all CPUs and run this function + * __stop_machine: freeze the machine on all CPUs and run this function * @fn: the function to run * @data: the data ptr for the @fn - * @cpu: the cpu to run @fn on (or any, if @cpu == NR_CPUS. + * @cpus: the cpus to run the @fn() on (NULL = any online cpu) * * Description: This is a special version of the above, which assumes cpus * won't come or go while it's being called. Used by hotplug cpu. */ -int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu); +int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus); #else -static inline int stop_machine_run(int (*fn)(void *), void *data, - unsigned int cpu) +static inline int stop_machine(int (*fn)(void *), void *data, + const cpumask_t *cpus) { int ret; local_irq_disable(); @@ -50,4 +50,18 @@ static inline int stop_machine_run(int (*fn)(void *), void *data, return ret; } #endif /* CONFIG_SMP */ + +static inline int __deprecated stop_machine_run(int (*fn)(void *), void *data, + unsigned int cpu) +{ + /* If they don't care which cpu fn runs on, just pick one. */ + if (cpu == NR_CPUS) + return stop_machine(fn, data, NULL); + else if (cpu == ~0U) + return stop_machine(fn, data, &cpu_possible_map); + else { + cpumask_t cpus = cpumask_of_cpu(cpu); + return stop_machine(fn, data, &cpus); + } +} #endif /* _LINUX_STOP_MACHINE */ diff --git a/kernel/cpu.c b/kernel/cpu.c index 53cf508f975a..29510d68338a 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -248,8 +248,9 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) cpus_setall(tmp); cpu_clear(cpu, tmp); set_cpus_allowed_ptr(current, &tmp); + tmp = cpumask_of_cpu(cpu); - err = __stop_machine_run(take_cpu_down, &tcd_param, cpu); + err = __stop_machine(take_cpu_down, &tcd_param, &tmp); if (err) { /* CPU didn't die: tell everyone. Can't complain. */ if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod, diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index 35882dccc943..e446c7c7d6a9 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -100,7 +100,7 @@ static int chill(void *unused) return 0; } -int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) +int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) { int i, err; struct stop_machine_data active, idle; @@ -112,10 +112,6 @@ int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) idle.fn = chill; idle.data = NULL; - /* If they don't care which cpu fn runs on, just pick one. */ - if (cpu == NR_CPUS) - cpu = any_online_cpu(cpu_online_map); - /* This could be too big for stack on large machines. */ threads = kcalloc(NR_CPUS, sizeof(threads[0]), GFP_KERNEL); if (!threads) @@ -128,13 +124,16 @@ int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) set_state(STOPMACHINE_PREPARE); for_each_online_cpu(i) { - struct stop_machine_data *smdata; + struct stop_machine_data *smdata = &idle; struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - if (cpu == ALL_CPUS || i == cpu) - smdata = &active; - else - smdata = &idle; + if (!cpus) { + if (i == first_cpu(cpu_online_map)) + smdata = &active; + } else { + if (cpu_isset(i, *cpus)) + smdata = &active; + } threads[i] = kthread_create((void *)stop_cpu, smdata, "kstop%u", i); @@ -154,7 +153,7 @@ int __stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) /* We've created all the threads. Wake them all: hold this CPU so one * doesn't hit this CPU until we're ready. */ - cpu = get_cpu(); + get_cpu(); for_each_online_cpu(i) wake_up_process(threads[i]); @@ -177,15 +176,15 @@ kill_threads: return err; } -int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu) +int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus) { int ret; /* No CPUs can come up or down during this. */ get_online_cpus(); - ret = __stop_machine_run(fn, data, cpu); + ret = __stop_machine(fn, data, cpus); put_online_cpus(); return ret; } -EXPORT_SYMBOL_GPL(stop_machine_run); +EXPORT_SYMBOL_GPL(stop_machine); -- cgit From 306cfd630a4d121cf4e08b894d8b4c4cf106e57e Mon Sep 17 00:00:00 2001 From: Adrian McMenamin Date: Sun, 15 Jun 2008 20:48:09 +0100 Subject: maple: tidy maple_driver code by removing redundant connect/disconnect The connect and disconnect functions are unnecessary - everything they do can be accomplished in the initial probe - so remove them. Signed-off-by: Adrian McMenamin Signed-off-by: Paul Mundt --- include/linux/maple.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/maple.h b/include/linux/maple.h index d31e36ebb436..523a286bb477 100644 --- a/include/linux/maple.h +++ b/include/linux/maple.h @@ -61,8 +61,6 @@ struct maple_device { struct maple_driver { unsigned long function; - int (*connect) (struct maple_device * dev); - void (*disconnect) (struct maple_device * dev); struct device_driver drv; }; -- cgit From 7f71ac9374fec066e428892a68db158946cee1fb Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 28 Jul 2008 18:29:09 +0200 Subject: mfd: Coding style fixes Fix some coding style fixes in the mfd core driver. Signed-off-by: Ben Dooks Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 15 +++++++-------- include/linux/mfd/core.h | 17 ++++++++--------- 2 files changed, 15 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 4dc861a7ac56..50207700140c 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -16,9 +16,9 @@ #include static int mfd_add_device(struct platform_device *parent, - const struct mfd_cell *cell, - struct resource *mem_base, - int irq_base) + const struct mfd_cell *cell, + struct resource *mem_base, + int irq_base) { struct resource res[cell->num_resources]; struct platform_device *pdev; @@ -75,11 +75,10 @@ fail_alloc: return ret; } -int mfd_add_devices( - struct platform_device *parent, - const struct mfd_cell *cells, int n_devs, - struct resource *mem_base, - int irq_base) +int mfd_add_devices(struct platform_device *parent, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base) { int i; int ret = 0; diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index bb3dd0545928..b7cbb9968339 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -1,5 +1,3 @@ -#ifndef MFD_CORE_H -#define MFD_CORE_H /* * drivers/mfd/mfd-core.h * @@ -13,6 +11,9 @@ * */ +#ifndef MFD_CORE_H +#define MFD_CORE_H + #include /* @@ -38,17 +39,15 @@ struct mfd_cell { const struct resource *resources; }; -static inline struct mfd_cell * -mfd_get_cell(struct platform_device *pdev) +static inline struct mfd_cell *mfd_get_cell(struct platform_device *pdev) { return (struct mfd_cell *)pdev->dev.platform_data; } -extern int mfd_add_devices( - struct platform_device *parent, - const struct mfd_cell *cells, int n_devs, - struct resource *mem_base, - int irq_base); +extern int mfd_add_devices(struct platform_device *parent, + const struct mfd_cell *cells, int n_devs, + struct resource *mem_base, + int irq_base); extern void mfd_remove_devices(struct platform_device *parent); -- cgit From e56b3bc7942982ac2589c942fb345e38bc7a341a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 28 Jul 2008 11:32:33 -0700 Subject: cpu masks: optimize and clean up cpumask_of_cpu() Clean up and optimize cpumask_of_cpu(), by sharing all the zero words. Instead of stupidly generating all possible i=0...NR_CPUS 2^i patterns creating a huge array of constant bitmasks, realize that the zero words can be shared. In other words, on a 64-bit architecture, we only ever need 64 of these arrays - with a different bit set in one single world (with enough zero words around it so that we can create any bitmask by just offsetting in that big array). And then we just put enough zeroes around it that we can point every single cpumask to be one of those things. So when we have 4k CPU's, instead of having 4k arrays (of 4k bits each, with one bit set in each array - 2MB memory total), we have exactly 64 arrays instead, each 8k bits in size (64kB total). And then we just point cpumask(n) to the right position (which we can calculate dynamically). Once we have the right arrays, getting "cpumask(n)" ends up being: static inline const cpumask_t *get_cpu_mask(unsigned int cpu) { const unsigned long *p = cpu_bit_bitmap[1 + cpu % BITS_PER_LONG]; p -= cpu / BITS_PER_LONG; return (const cpumask_t *)p; } This brings other advantages and simplifications as well: - we are not wasting memory that is just filled with a single bit in various different places - we don't need all those games to re-create the arrays in some dense format, because they're already going to be dense enough. if we compile a kernel for up to 4k CPU's, "wasting" that 64kB of memory is a non-issue (especially since by doing this "overlapping" trick we probably get better cache behaviour anyway). [ mingo@elte.hu: Converted Linus's mails into a commit. See: http://lkml.org/lkml/2008/7/27/156 http://lkml.org/lkml/2008/7/28/320 Also applied a family filter - which also has the side-effect of leaving out the bits where Linus calls me an idio... Oh, never mind ;-) ] Signed-off-by: Ingo Molnar Cc: Rusty Russell Cc: Andrew Morton Cc: Al Viro Cc: Mike Travis Signed-off-by: Ingo Molnar --- arch/x86/kernel/setup_percpu.c | 23 -------- include/linux/cpumask.h | 26 ++++++++- kernel/cpu.c | 128 +++++++---------------------------------- 3 files changed, 43 insertions(+), 134 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c index 1cd53dfcd309..76e305e064f9 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c @@ -80,26 +80,6 @@ static void __init setup_per_cpu_maps(void) #endif } -#ifdef CONFIG_HAVE_CPUMASK_OF_CPU_MAP -/* - * Replace static cpumask_of_cpu_map in the initdata section, - * with one that's allocated sized by the possible number of cpus. - * - * (requires nr_cpu_ids to be initialized) - */ -static void __init setup_cpumask_of_cpu(void) -{ - int i; - - /* alloc_bootmem zeroes memory */ - cpumask_of_cpu_map = alloc_bootmem_low(sizeof(cpumask_t) * nr_cpu_ids); - for (i = 0; i < nr_cpu_ids; i++) - cpu_set(i, cpumask_of_cpu_map[i]); -} -#else -static inline void setup_cpumask_of_cpu(void) { } -#endif - #ifdef CONFIG_X86_32 /* * Great future not-so-futuristic plan: make i386 and x86_64 do it @@ -199,9 +179,6 @@ void __init setup_per_cpu_areas(void) /* Setup node to cpumask map */ setup_node_to_cpumask_map(); - - /* Setup cpumask_of_cpu map */ - setup_cpumask_of_cpu(); } #endif diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 8fa3b6d4a320..96d0509fb8d8 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -265,10 +265,30 @@ static inline void __cpus_shift_left(cpumask_t *dstp, bitmap_shift_left(dstp->bits, srcp->bits, n, nbits); } +/* + * Special-case data structure for "single bit set only" constant CPU masks. + * + * We pre-generate all the 64 (or 32) possible bit positions, with enough + * padding to the left and the right, and return the constant pointer + * appropriately offset. + */ +extern const unsigned long + cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)]; + +static inline const cpumask_t *get_cpu_mask(unsigned int cpu) +{ + const unsigned long *p = cpu_bit_bitmap[1 + cpu % BITS_PER_LONG]; + p -= cpu / BITS_PER_LONG; + return (const cpumask_t *)p; +} + +/* + * In cases where we take the address of the cpumask immediately, + * gcc optimizes it out (it's a constant) and there's no huge stack + * variable created: + */ +#define cpumask_of_cpu(cpu) ({ *get_cpu_mask(cpu); }) -/* cpumask_of_cpu_map[] is in kernel/cpu.c */ -extern const cpumask_t *cpumask_of_cpu_map; -#define cpumask_of_cpu(cpu) (cpumask_of_cpu_map[cpu]) #define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS) diff --git a/kernel/cpu.c b/kernel/cpu.c index a35d8995dc8c..06a8358bb418 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -462,115 +462,27 @@ out: #endif /* CONFIG_SMP */ -/* 64 bits of zeros, for initializers. */ -#if BITS_PER_LONG == 32 -#define Z64 0, 0 -#else -#define Z64 0 -#endif +/* + * cpu_bit_bitmap[] is a special, "compressed" data structure that + * represents all NR_CPUS bits binary values of 1< 4 - CMI0(4), CMI0(5), CMI0(6), CMI0(7), -#endif -#if NR_CPUS > 8 - CMI0(8), CMI0(9), CMI0(10), CMI0(11), - CMI0(12), CMI0(13), CMI0(14), CMI0(15), -#endif -#if NR_CPUS > 16 - CMI0(16), CMI0(17), CMI0(18), CMI0(19), - CMI0(20), CMI0(21), CMI0(22), CMI0(23), - CMI0(24), CMI0(25), CMI0(26), CMI0(27), - CMI0(28), CMI0(29), CMI0(30), CMI0(31), -#endif -#if NR_CPUS > 32 -#if BITS_PER_LONG == 32 - CMI(32, 0), CMI(33, 0), CMI(34, 0), CMI(35, 0), - CMI(36, 0), CMI(37, 0), CMI(38, 0), CMI(39, 0), - CMI(40, 0), CMI(41, 0), CMI(42, 0), CMI(43, 0), - CMI(44, 0), CMI(45, 0), CMI(46, 0), CMI(47, 0), - CMI(48, 0), CMI(49, 0), CMI(50, 0), CMI(51, 0), - CMI(52, 0), CMI(53, 0), CMI(54, 0), CMI(55, 0), - CMI(56, 0), CMI(57, 0), CMI(58, 0), CMI(59, 0), - CMI(60, 0), CMI(61, 0), CMI(62, 0), CMI(63, 0), -#else - CMI0(32), CMI0(33), CMI0(34), CMI0(35), - CMI0(36), CMI0(37), CMI0(38), CMI0(39), - CMI0(40), CMI0(41), CMI0(42), CMI0(43), - CMI0(44), CMI0(45), CMI0(46), CMI0(47), - CMI0(48), CMI0(49), CMI0(50), CMI0(51), - CMI0(52), CMI0(53), CMI0(54), CMI0(55), - CMI0(56), CMI0(57), CMI0(58), CMI0(59), - CMI0(60), CMI0(61), CMI0(62), CMI0(63), -#endif /* BITS_PER_LONG == 64 */ -#endif -#if NR_CPUS > 64 - CMI64(64, Z64), -#endif -#if NR_CPUS > 128 - CMI64(128, Z64, Z64), CMI64(192, Z64, Z64, Z64), -#endif -#if NR_CPUS > 256 - CMI256(256, Z256), -#endif -#if NR_CPUS > 512 - CMI256(512, Z256, Z256), CMI256(768, Z256, Z256, Z256), -#endif -#if NR_CPUS > 1024 - CMI1024(1024, Z1024), -#endif -#if NR_CPUS > 2048 - CMI1024(2048, Z1024, Z1024), CMI1024(3072, Z1024, Z1024, Z1024), -#endif -#if NR_CPUS > 4096 -#error NR_CPUS too big. Fix initializers or set CONFIG_HAVE_CPUMASK_OF_CPU_MAP +const unsigned long cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)] = { + + MASK_DECLARE_8(0), MASK_DECLARE_8(8), + MASK_DECLARE_8(16), MASK_DECLARE_8(24), +#if BITS_PER_LONG > 32 + MASK_DECLARE_8(32), MASK_DECLARE_8(40), + MASK_DECLARE_8(48), MASK_DECLARE_8(56), #endif }; - -const cpumask_t *cpumask_of_cpu_map = cpumask_map; - -EXPORT_SYMBOL_GPL(cpumask_of_cpu_map); +EXPORT_SYMBOL_GPL(cpu_bit_bitmap); -- cgit From 5fde244d39b88625ac578d83e6625138714de031 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Wed, 23 Jul 2008 10:32:24 +0800 Subject: PCI: disable ASPM per ACPI FADT setting The ACPI FADT table includes an ASPM control bit. If the bit is set, do not enable ASPM since it may indicate that the platform doesn't actually support the feature. Tested-by: Jack Howarth Signed-off-by: Shaohua Li Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 7 +++++++ drivers/pci/pcie/aspm.c | 5 +++++ include/acpi/actbl.h | 1 + include/linux/pci-aspm.h | 5 +++++ 4 files changed, 18 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 7764768b6a0e..89a2f0fa10f9 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -372,6 +373,12 @@ static int __init acpi_pci_init(void) printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n"); pci_no_msi(); } + + if (acpi_gbl_FADT.boot_flags & BAF_PCIE_ASPM_CONTROL) { + printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); + pcie_no_aspm(); + } + ret = register_acpi_bus_type(&acpi_pci_bus); if (ret) return 0; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index f82495583e63..759c51a4e399 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -808,6 +808,11 @@ static int __init pcie_aspm_disable(char *str) __setup("pcie_noaspm", pcie_aspm_disable); +void pcie_no_aspm(void) +{ + aspm_disabled = 1; +} + #ifdef CONFIG_ACPI #include #include diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index 1ebbe883f786..13a3d9ad92db 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -277,6 +277,7 @@ enum acpi_prefered_pm_profiles { #define BAF_LEGACY_DEVICES 0x0001 #define BAF_8042_KEYBOARD_CONTROLLER 0x0002 #define BAF_MSI_NOT_SUPPORTED 0x0008 +#define BAF_PCIE_ASPM_CONTROL 0x0010 #define FADT2_REVISION_ID 3 #define FADT2_MINUS_REVISION_ID 2 diff --git a/include/linux/pci-aspm.h b/include/linux/pci-aspm.h index a1a1e618e996..91ba0b338b47 100644 --- a/include/linux/pci-aspm.h +++ b/include/linux/pci-aspm.h @@ -27,6 +27,7 @@ extern void pcie_aspm_init_link_state(struct pci_dev *pdev); extern void pcie_aspm_exit_link_state(struct pci_dev *pdev); extern void pcie_aspm_pm_state_change(struct pci_dev *pdev); extern void pci_disable_link_state(struct pci_dev *pdev, int state); +extern void pcie_no_aspm(void); #else static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { @@ -40,6 +41,10 @@ static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev) static inline void pci_disable_link_state(struct pci_dev *pdev, int state) { } + +static inline void pcie_no_aspm(void) +{ +} #endif #ifdef CONFIG_PCIEASPM_DEBUG /* this depends on CONFIG_PCIEASPM */ -- cgit From 149e16372a2066c5474d8a8db9b252afd57eb427 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Wed, 23 Jul 2008 10:32:31 +0800 Subject: PCI: disable ASPM on pre-1.1 PCIe devices Disable ASPM on pre-1.1 PCIe devices, as many of them don't implement it correctly. Tested-by: Jack Howarth Signed-off-by: Shaohua Li Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 13 +++++++++++++ drivers/pci/probe.c | 3 ++- include/linux/pci_regs.h | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 759c51a4e399..704605298c5e 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -510,6 +510,7 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) { struct pci_dev *child_dev; int child_pos; + u32 reg32; /* * Some functions in a slot might not all be PCIE functions, very @@ -519,6 +520,18 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); if (!child_pos) return -EINVAL; + + /* + * Disable ASPM for pre-1.1 PCIe device, we follow MS to use + * RBER bit to determine if a function is 1.1 version device + */ + pci_read_config_dword(child_dev, child_pos + PCI_EXP_DEVCAP, + ®32); + if (!(reg32 & PCI_EXP_DEVCAP_RBER)) { + printk("Pre-1.1 PCIe device detected, " + "disable ASPM for %s\n", pci_name(pdev)); + return -EINVAL; + } } return 0; } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 203630065839..7098dfb07449 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1057,7 +1057,8 @@ int pci_scan_slot(struct pci_bus *bus, int devfn) } } - if (bus->self) + /* only one slot has pcie device */ + if (bus->self && nr) pcie_aspm_init_link_state(bus->self); return nr; diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 19958b929905..450684f7eaac 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -374,6 +374,7 @@ #define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ #define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ #define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ #define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ #define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ #define PCI_EXP_DEVCTL 8 /* Device Control */ -- cgit From 979b1791e5b8f8b556faeec4c48339e7ed63af9f Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 24 Jul 2008 17:18:38 +0100 Subject: PCI: add D3 power state avoidance quirk Libata has some hacks to deal with certain controllers going silly in D3 state. The right way to handle this is to keep a PCI device flag for such devices. That can then be generalised for no ATA devices with power problems. Signed-off-by: Alan Cox Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 4 ++++ drivers/pci/quirks.c | 13 +++++++++++++ include/linux/pci.h | 2 ++ 3 files changed, 19 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c95f77d65718..0a3d856833fc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -572,6 +572,10 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (!ret) pci_update_current_state(dev); } + /* This device is quirked not to be put into D3, so + don't put it in D3 */ + if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) + return 0; error = pci_raw_set_power_state(dev, state); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 12d489395fad..0fb365074288 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -923,6 +923,19 @@ static void __init quirk_ide_samemode(struct pci_dev *pdev) } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, quirk_ide_samemode); +/* + * Some ATA devices break if put into D3 + */ + +static void __devinit quirk_no_ata_d3(struct pci_dev *pdev) +{ + /* Quirk the legacy ATA devices only. The AHCI ones are ok */ + if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) + pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_ANY_ID, quirk_no_ata_d3); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, PCI_ANY_ID, quirk_no_ata_d3); + /* This was originally an Alpha specific thing, but it really fits here. * The i82375 PCI/EISA bridge appears as non-classified. Fix that. */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 1d296d31abe0..825be3878f68 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -124,6 +124,8 @@ enum pci_dev_flags { * generation too. */ PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1, + /* Device configuration is irrevocably lost if disabled into D3 */ + PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2, }; typedef unsigned short __bitwise pci_bus_flags_t; -- cgit From 56edb58be157a06dc147a988af3588059556d392 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 29 Jul 2008 01:23:32 +0200 Subject: mfd: add platform_data to mfd_cell Adding platform_data to mfd_cell allows passing of platform data directly to the platform_device created for each cell and thus reuse of existing drivers. On the other side it can be used as a hook to mfd_cell itself removing the need in mfd_get_cell method. Signed-off-by: Mike Rapoport Acked-by: Dmitry Baryshkov Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 2 +- drivers/mfd/tc6393xb.c | 4 ++++ include/linux/mfd/core.h | 13 +++++++------ 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 50207700140c..ad4e4d16a36a 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -32,7 +32,7 @@ static int mfd_add_device(struct platform_device *parent, pdev->dev.parent = &parent->dev; ret = platform_device_add_data(pdev, - cell, sizeof(struct mfd_cell)); + cell->platform_data, cell->data_size); if (ret) goto fail_device; diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 94e55e8e7ce6..9908aaa4881a 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -466,6 +466,10 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) tc6393xb_attach_irq(dev); tc6393xb_cells[TC6393XB_CELL_NAND].driver_data = tcpd->nand_data; + tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = + &tc6393xb_cells[TC6393XB_CELL_NAND]; + tc6393xb_cells[TC6393XB_CELL_NAND].data_size = + sizeof(tc6393xb_cells[TC6393XB_CELL_NAND]); retval = mfd_add_devices(dev, tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index b7cbb9968339..ea45d4a5a2ac 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -29,7 +29,13 @@ struct mfd_cell { int (*suspend)(struct platform_device *dev); int (*resume)(struct platform_device *dev); - void *driver_data; /* driver-specific data */ + /* driver-specific data for MFD-aware "cell" drivers */ + void *driver_data; + + /* platform_data can be used to either pass data to "generic" + driver or as a hook to mfd_cell for the "cell" drivers */ + void *platform_data; + size_t data_size; /* * This resources can be specified relatievly to the parent device. @@ -39,11 +45,6 @@ struct mfd_cell { const struct resource *resources; }; -static inline struct mfd_cell *mfd_get_cell(struct platform_device *pdev) -{ - return (struct mfd_cell *)pdev->dev.platform_data; -} - extern int mfd_add_devices(struct platform_device *parent, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, -- cgit From 6beeac76f5f96590fb751af5e138fbc3f62e8460 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Mon, 28 Jul 2008 15:46:22 -0700 Subject: mmu-notifiers: add list_del_init_rcu() Introduce list_del_init_rcu() and document it. Signed-off-by: Andrea Arcangeli Acked-by: Linus Torvalds Cc: "Paul E. McKenney" Cc: Ingo Molnar Cc: Christoph Lameter Cc: Jack Steiner Cc: Robin Holt Cc: Nick Piggin Cc: Peter Zijlstra Cc: Kanoj Sarcar Cc: Roland Dreier Cc: Steve Wise Cc: Avi Kivity Cc: Hugh Dickins Cc: Rusty Russell Cc: Anthony Liguori Cc: Chris Wright Cc: Marcelo Tosatti Cc: Eric Dumazet Cc: "Paul E. McKenney" Cc: Izik Eidus Cc: Anthony Liguori Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rculist.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'include/linux') diff --git a/include/linux/rculist.h b/include/linux/rculist.h index b0f39be08b6c..eb4443c7e05b 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -97,6 +97,34 @@ static inline void list_del_rcu(struct list_head *entry) entry->prev = LIST_POISON2; } +/** + * hlist_del_init_rcu - deletes entry from hash list with re-initialization + * @n: the element to delete from the hash list. + * + * Note: list_unhashed() on the node return true after this. It is + * useful for RCU based read lockfree traversal if the writer side + * must know if the list entry is still hashed or already unhashed. + * + * In particular, it means that we can not poison the forward pointers + * that may still be used for walking the hash list and we can only + * zero the pprev pointer so list_unhashed() will return true after + * this. + * + * The caller must take whatever precautions are necessary (such as + * holding appropriate locks) to avoid racing with another + * list-mutation primitive, such as hlist_add_head_rcu() or + * hlist_del_rcu(), running on this same list. However, it is + * perfectly legal to run concurrently with the _rcu list-traversal + * primitives, such as hlist_for_each_entry_rcu(). + */ +static inline void hlist_del_init_rcu(struct hlist_node *n) +{ + if (!hlist_unhashed(n)) { + __hlist_del(n); + n->pprev = NULL; + } +} + /** * list_replace_rcu - replace old entry by new one * @old : the element to be replaced -- cgit From 7906d00cd1f687268f0a3599442d113767795ae6 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Mon, 28 Jul 2008 15:46:26 -0700 Subject: mmu-notifiers: add mm_take_all_locks() operation mm_take_all_locks holds off reclaim from an entire mm_struct. This allows mmu notifiers to register into the mm at any time with the guarantee that no mmu operation is in progress on the mm. This operation locks against the VM for all pte/vma/mm related operations that could ever happen on a certain mm. This includes vmtruncate, try_to_unmap, and all page faults. The caller must take the mmap_sem in write mode before calling mm_take_all_locks(). The caller isn't allowed to release the mmap_sem until mm_drop_all_locks() returns. mmap_sem in write mode is required in order to block all operations that could modify pagetables and free pages without need of altering the vma layout (for example populate_range() with nonlinear vmas). It's also needed in write mode to avoid new anon_vmas to be associated with existing vmas. A single task can't take more than one mm_take_all_locks() in a row or it would deadlock. mm_take_all_locks() and mm_drop_all_locks are expensive operations that may have to take thousand of locks. mm_take_all_locks() can fail if it's interrupted by signals. When mmu_notifier_register returns, we must be sure that the driver is notified if some task is in the middle of a vmtruncate for the 'mm' where the mmu notifier was registered (mmu_notifier_invalidate_range_start/end is run around the vmtruncation but mmu_notifier_register can run after mmu_notifier_invalidate_range_start and before mmu_notifier_invalidate_range_end). Same problem for rmap paths. And we've to remove page pinning to avoid replicating the tlb_gather logic inside KVM (and GRU doesn't work well with page pinning regardless of needing tlb_gather), so without mm_take_all_locks when vmtruncate frees the page, kvm would have no way to notice that it mapped into sptes a page that is going into the freelist without a chance of any further mmu_notifier notification. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Andrea Arcangeli Acked-by: Linus Torvalds Cc: Christoph Lameter Cc: Jack Steiner Cc: Robin Holt Cc: Nick Piggin Cc: Peter Zijlstra Cc: Kanoj Sarcar Cc: Roland Dreier Cc: Steve Wise Cc: Avi Kivity Cc: Hugh Dickins Cc: Rusty Russell Cc: Anthony Liguori Cc: Chris Wright Cc: Marcelo Tosatti Cc: Eric Dumazet Cc: "Paul E. McKenney" Cc: Izik Eidus Cc: Anthony Liguori Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 3 + include/linux/pagemap.h | 1 + include/linux/rmap.h | 8 +++ mm/mmap.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 6e695eaab4ce..866a3dbe5c75 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1104,6 +1104,9 @@ extern struct vm_area_struct *copy_vma(struct vm_area_struct **, unsigned long addr, unsigned long len, pgoff_t pgoff); extern void exit_mmap(struct mm_struct *); +extern int mm_take_all_locks(struct mm_struct *mm); +extern void mm_drop_all_locks(struct mm_struct *mm); + #ifdef CONFIG_PROC_FS /* From fs/proc/base.c. callers must _not_ hold the mm's exe_file_lock */ extern void added_exe_file_vma(struct mm_struct *mm); diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index a81d81890422..a39b38ccdc97 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -20,6 +20,7 @@ */ #define AS_EIO (__GFP_BITS_SHIFT + 0) /* IO error on async write */ #define AS_ENOSPC (__GFP_BITS_SHIFT + 1) /* ENOSPC on async write */ +#define AS_MM_ALL_LOCKS (__GFP_BITS_SHIFT + 2) /* under mm_take_all_locks() */ static inline void mapping_set_error(struct address_space *mapping, int error) { diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 1383692ac5bd..69407f85e10b 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -26,6 +26,14 @@ */ struct anon_vma { spinlock_t lock; /* Serialize access to vma list */ + /* + * NOTE: the LSB of the head.next is set by + * mm_take_all_locks() _after_ taking the above lock. So the + * head must only be read/written after taking the above lock + * to be sure to see a valid next pointer. The LSB bit itself + * is serialized by a system wide lock only visible to + * mm_take_all_locks() (mm_all_locks_mutex). + */ struct list_head head; /* List of private "related" vmas */ }; diff --git a/mm/mmap.c b/mm/mmap.c index 5e0cc99e9cd5..e5f9cb83d6d4 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2268,3 +2268,161 @@ int install_special_mapping(struct mm_struct *mm, return 0; } + +static DEFINE_MUTEX(mm_all_locks_mutex); + +static void vm_lock_anon_vma(struct anon_vma *anon_vma) +{ + if (!test_bit(0, (unsigned long *) &anon_vma->head.next)) { + /* + * The LSB of head.next can't change from under us + * because we hold the mm_all_locks_mutex. + */ + spin_lock(&anon_vma->lock); + /* + * We can safely modify head.next after taking the + * anon_vma->lock. If some other vma in this mm shares + * the same anon_vma we won't take it again. + * + * No need of atomic instructions here, head.next + * can't change from under us thanks to the + * anon_vma->lock. + */ + if (__test_and_set_bit(0, (unsigned long *) + &anon_vma->head.next)) + BUG(); + } +} + +static void vm_lock_mapping(struct address_space *mapping) +{ + if (!test_bit(AS_MM_ALL_LOCKS, &mapping->flags)) { + /* + * AS_MM_ALL_LOCKS can't change from under us because + * we hold the mm_all_locks_mutex. + * + * Operations on ->flags have to be atomic because + * even if AS_MM_ALL_LOCKS is stable thanks to the + * mm_all_locks_mutex, there may be other cpus + * changing other bitflags in parallel to us. + */ + if (test_and_set_bit(AS_MM_ALL_LOCKS, &mapping->flags)) + BUG(); + spin_lock(&mapping->i_mmap_lock); + } +} + +/* + * This operation locks against the VM for all pte/vma/mm related + * operations that could ever happen on a certain mm. This includes + * vmtruncate, try_to_unmap, and all page faults. + * + * The caller must take the mmap_sem in write mode before calling + * mm_take_all_locks(). The caller isn't allowed to release the + * mmap_sem until mm_drop_all_locks() returns. + * + * mmap_sem in write mode is required in order to block all operations + * that could modify pagetables and free pages without need of + * altering the vma layout (for example populate_range() with + * nonlinear vmas). It's also needed in write mode to avoid new + * anon_vmas to be associated with existing vmas. + * + * A single task can't take more than one mm_take_all_locks() in a row + * or it would deadlock. + * + * The LSB in anon_vma->head.next and the AS_MM_ALL_LOCKS bitflag in + * mapping->flags avoid to take the same lock twice, if more than one + * vma in this mm is backed by the same anon_vma or address_space. + * + * We can take all the locks in random order because the VM code + * taking i_mmap_lock or anon_vma->lock outside the mmap_sem never + * takes more than one of them in a row. Secondly we're protected + * against a concurrent mm_take_all_locks() by the mm_all_locks_mutex. + * + * mm_take_all_locks() and mm_drop_all_locks are expensive operations + * that may have to take thousand of locks. + * + * mm_take_all_locks() can fail if it's interrupted by signals. + */ +int mm_take_all_locks(struct mm_struct *mm) +{ + struct vm_area_struct *vma; + int ret = -EINTR; + + BUG_ON(down_read_trylock(&mm->mmap_sem)); + + mutex_lock(&mm_all_locks_mutex); + + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (signal_pending(current)) + goto out_unlock; + if (vma->anon_vma) + vm_lock_anon_vma(vma->anon_vma); + if (vma->vm_file && vma->vm_file->f_mapping) + vm_lock_mapping(vma->vm_file->f_mapping); + } + ret = 0; + +out_unlock: + if (ret) + mm_drop_all_locks(mm); + + return ret; +} + +static void vm_unlock_anon_vma(struct anon_vma *anon_vma) +{ + if (test_bit(0, (unsigned long *) &anon_vma->head.next)) { + /* + * The LSB of head.next can't change to 0 from under + * us because we hold the mm_all_locks_mutex. + * + * We must however clear the bitflag before unlocking + * the vma so the users using the anon_vma->head will + * never see our bitflag. + * + * No need of atomic instructions here, head.next + * can't change from under us until we release the + * anon_vma->lock. + */ + if (!__test_and_clear_bit(0, (unsigned long *) + &anon_vma->head.next)) + BUG(); + spin_unlock(&anon_vma->lock); + } +} + +static void vm_unlock_mapping(struct address_space *mapping) +{ + if (test_bit(AS_MM_ALL_LOCKS, &mapping->flags)) { + /* + * AS_MM_ALL_LOCKS can't change to 0 from under us + * because we hold the mm_all_locks_mutex. + */ + spin_unlock(&mapping->i_mmap_lock); + if (!test_and_clear_bit(AS_MM_ALL_LOCKS, + &mapping->flags)) + BUG(); + } +} + +/* + * The mmap_sem cannot be released by the caller until + * mm_drop_all_locks() returns. + */ +void mm_drop_all_locks(struct mm_struct *mm) +{ + struct vm_area_struct *vma; + + BUG_ON(down_read_trylock(&mm->mmap_sem)); + BUG_ON(!mutex_is_locked(&mm_all_locks_mutex)); + + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (vma->anon_vma) + vm_unlock_anon_vma(vma->anon_vma); + if (vma->vm_file && vma->vm_file->f_mapping) + vm_unlock_mapping(vma->vm_file->f_mapping); + } + + mutex_unlock(&mm_all_locks_mutex); +} -- cgit From cddb8a5c14aa89810b40495d94d3d2a0faee6619 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Mon, 28 Jul 2008 15:46:29 -0700 Subject: mmu-notifiers: core With KVM/GFP/XPMEM there isn't just the primary CPU MMU pointing to pages. There are secondary MMUs (with secondary sptes and secondary tlbs) too. sptes in the kvm case are shadow pagetables, but when I say spte in mmu-notifier context, I mean "secondary pte". In GRU case there's no actual secondary pte and there's only a secondary tlb because the GRU secondary MMU has no knowledge about sptes and every secondary tlb miss event in the MMU always generates a page fault that has to be resolved by the CPU (this is not the case of KVM where the a secondary tlb miss will walk sptes in hardware and it will refill the secondary tlb transparently to software if the corresponding spte is present). The same way zap_page_range has to invalidate the pte before freeing the page, the spte (and secondary tlb) must also be invalidated before any page is freed and reused. Currently we take a page_count pin on every page mapped by sptes, but that means the pages can't be swapped whenever they're mapped by any spte because they're part of the guest working set. Furthermore a spte unmap event can immediately lead to a page to be freed when the pin is released (so requiring the same complex and relatively slow tlb_gather smp safe logic we have in zap_page_range and that can be avoided completely if the spte unmap event doesn't require an unpin of the page previously mapped in the secondary MMU). The mmu notifiers allow kvm/GRU/XPMEM to attach to the tsk->mm and know when the VM is swapping or freeing or doing anything on the primary MMU so that the secondary MMU code can drop sptes before the pages are freed, avoiding all page pinning and allowing 100% reliable swapping of guest physical address space. Furthermore it avoids the code that teardown the mappings of the secondary MMU, to implement a logic like tlb_gather in zap_page_range that would require many IPI to flush other cpu tlbs, for each fixed number of spte unmapped. To make an example: if what happens on the primary MMU is a protection downgrade (from writeable to wrprotect) the secondary MMU mappings will be invalidated, and the next secondary-mmu-page-fault will call get_user_pages and trigger a do_wp_page through get_user_pages if it called get_user_pages with write=1, and it'll re-establishing an updated spte or secondary-tlb-mapping on the copied page. Or it will setup a readonly spte or readonly tlb mapping if it's a guest-read, if it calls get_user_pages with write=0. This is just an example. This allows to map any page pointed by any pte (and in turn visible in the primary CPU MMU), into a secondary MMU (be it a pure tlb like GRU, or an full MMU with both sptes and secondary-tlb like the shadow-pagetable layer with kvm), or a remote DMA in software like XPMEM (hence needing of schedule in XPMEM code to send the invalidate to the remote node, while no need to schedule in kvm/gru as it's an immediate event like invalidating primary-mmu pte). At least for KVM without this patch it's impossible to swap guests reliably. And having this feature and removing the page pin allows several other optimizations that simplify life considerably. Dependencies: 1) mm_take_all_locks() to register the mmu notifier when the whole VM isn't doing anything with "mm". This allows mmu notifier users to keep track if the VM is in the middle of the invalidate_range_begin/end critical section with an atomic counter incraese in range_begin and decreased in range_end. No secondary MMU page fault is allowed to map any spte or secondary tlb reference, while the VM is in the middle of range_begin/end as any page returned by get_user_pages in that critical section could later immediately be freed without any further ->invalidate_page notification (invalidate_range_begin/end works on ranges and ->invalidate_page isn't called immediately before freeing the page). To stop all page freeing and pagetable overwrites the mmap_sem must be taken in write mode and all other anon_vma/i_mmap locks must be taken too. 2) It'd be a waste to add branches in the VM if nobody could possibly run KVM/GRU/XPMEM on the kernel, so mmu notifiers will only enabled if CONFIG_KVM=m/y. In the current kernel kvm won't yet take advantage of mmu notifiers, but this already allows to compile a KVM external module against a kernel with mmu notifiers enabled and from the next pull from kvm.git we'll start using them. And GRU/XPMEM will also be able to continue the development by enabling KVM=m in their config, until they submit all GRU/XPMEM GPLv2 code to the mainline kernel. Then they can also enable MMU_NOTIFIERS in the same way KVM does it (even if KVM=n). This guarantees nobody selects MMU_NOTIFIER=y if KVM and GRU and XPMEM are all =n. The mmu_notifier_register call can fail because mm_take_all_locks may be interrupted by a signal and return -EINTR. Because mmu_notifier_reigster is used when a driver startup, a failure can be gracefully handled. Here an example of the change applied to kvm to register the mmu notifiers. Usually when a driver startups other allocations are required anyway and -ENOMEM failure paths exists already. struct kvm *kvm_arch_create_vm(void) { struct kvm *kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL); + int err; if (!kvm) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&kvm->arch.active_mmu_pages); + kvm->arch.mmu_notifier.ops = &kvm_mmu_notifier_ops; + err = mmu_notifier_register(&kvm->arch.mmu_notifier, current->mm); + if (err) { + kfree(kvm); + return ERR_PTR(err); + } + return kvm; } mmu_notifier_unregister returns void and it's reliable. The patch also adds a few needed but missing includes that would prevent kernel to compile after these changes on non-x86 archs (x86 didn't need them by luck). [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: fix mm/filemap_xip.c build] [akpm@linux-foundation.org: fix mm/mmu_notifier.c build] Signed-off-by: Andrea Arcangeli Signed-off-by: Nick Piggin Signed-off-by: Christoph Lameter Cc: Jack Steiner Cc: Robin Holt Cc: Nick Piggin Cc: Peter Zijlstra Cc: Kanoj Sarcar Cc: Roland Dreier Cc: Steve Wise Cc: Avi Kivity Cc: Hugh Dickins Cc: Rusty Russell Cc: Anthony Liguori Cc: Chris Wright Cc: Marcelo Tosatti Cc: Eric Dumazet Cc: "Paul E. McKenney" Cc: Izik Eidus Cc: Anthony Liguori Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/kvm/Kconfig | 1 + include/linux/mm_types.h | 4 + include/linux/mmu_notifier.h | 279 +++++++++++++++++++++++++++++++++++++++++++ kernel/fork.c | 3 + mm/Kconfig | 3 + mm/Makefile | 1 + mm/filemap_xip.c | 3 +- mm/fremap.c | 3 + mm/hugetlb.c | 3 + mm/memory.c | 35 +++++- mm/mmap.c | 2 + mm/mmu_notifier.c | 277 ++++++++++++++++++++++++++++++++++++++++++ mm/mprotect.c | 3 + mm/mremap.c | 6 + mm/rmap.c | 13 +- 15 files changed, 623 insertions(+), 13 deletions(-) create mode 100644 include/linux/mmu_notifier.h create mode 100644 mm/mmu_notifier.c (limited to 'include/linux') diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index 8d45fabc5f3b..ce3251ce5504 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -21,6 +21,7 @@ config KVM tristate "Kernel-based Virtual Machine (KVM) support" depends on HAVE_KVM select PREEMPT_NOTIFIERS + select MMU_NOTIFIER select ANON_INODES ---help--- Support hosting fully virtualized guest machines using hardware diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 746f975b58ef..386edbe2cb4e 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -253,6 +254,9 @@ struct mm_struct { struct file *exe_file; unsigned long num_exe_file_vmas; #endif +#ifdef CONFIG_MMU_NOTIFIER + struct mmu_notifier_mm *mmu_notifier_mm; +#endif }; #endif /* _LINUX_MM_TYPES_H */ diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h new file mode 100644 index 000000000000..b77486d152cd --- /dev/null +++ b/include/linux/mmu_notifier.h @@ -0,0 +1,279 @@ +#ifndef _LINUX_MMU_NOTIFIER_H +#define _LINUX_MMU_NOTIFIER_H + +#include +#include +#include + +struct mmu_notifier; +struct mmu_notifier_ops; + +#ifdef CONFIG_MMU_NOTIFIER + +/* + * The mmu notifier_mm structure is allocated and installed in + * mm->mmu_notifier_mm inside the mm_take_all_locks() protected + * critical section and it's released only when mm_count reaches zero + * in mmdrop(). + */ +struct mmu_notifier_mm { + /* all mmu notifiers registerd in this mm are queued in this list */ + struct hlist_head list; + /* to serialize the list modifications and hlist_unhashed */ + spinlock_t lock; +}; + +struct mmu_notifier_ops { + /* + * Called either by mmu_notifier_unregister or when the mm is + * being destroyed by exit_mmap, always before all pages are + * freed. This can run concurrently with other mmu notifier + * methods (the ones invoked outside the mm context) and it + * should tear down all secondary mmu mappings and freeze the + * secondary mmu. If this method isn't implemented you've to + * be sure that nothing could possibly write to the pages + * through the secondary mmu by the time the last thread with + * tsk->mm == mm exits. + * + * As side note: the pages freed after ->release returns could + * be immediately reallocated by the gart at an alias physical + * address with a different cache model, so if ->release isn't + * implemented because all _software_ driven memory accesses + * through the secondary mmu are terminated by the time the + * last thread of this mm quits, you've also to be sure that + * speculative _hardware_ operations can't allocate dirty + * cachelines in the cpu that could not be snooped and made + * coherent with the other read and write operations happening + * through the gart alias address, so leading to memory + * corruption. + */ + void (*release)(struct mmu_notifier *mn, + struct mm_struct *mm); + + /* + * clear_flush_young is called after the VM is + * test-and-clearing the young/accessed bitflag in the + * pte. This way the VM will provide proper aging to the + * accesses to the page through the secondary MMUs and not + * only to the ones through the Linux pte. + */ + int (*clear_flush_young)(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long address); + + /* + * Before this is invoked any secondary MMU is still ok to + * read/write to the page previously pointed to by the Linux + * pte because the page hasn't been freed yet and it won't be + * freed until this returns. If required set_page_dirty has to + * be called internally to this method. + */ + void (*invalidate_page)(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long address); + + /* + * invalidate_range_start() and invalidate_range_end() must be + * paired and are called only when the mmap_sem and/or the + * locks protecting the reverse maps are held. The subsystem + * must guarantee that no additional references are taken to + * the pages in the range established between the call to + * invalidate_range_start() and the matching call to + * invalidate_range_end(). + * + * Invalidation of multiple concurrent ranges may be + * optionally permitted by the driver. Either way the + * establishment of sptes is forbidden in the range passed to + * invalidate_range_begin/end for the whole duration of the + * invalidate_range_begin/end critical section. + * + * invalidate_range_start() is called when all pages in the + * range are still mapped and have at least a refcount of one. + * + * invalidate_range_end() is called when all pages in the + * range have been unmapped and the pages have been freed by + * the VM. + * + * The VM will remove the page table entries and potentially + * the page between invalidate_range_start() and + * invalidate_range_end(). If the page must not be freed + * because of pending I/O or other circumstances then the + * invalidate_range_start() callback (or the initial mapping + * by the driver) must make sure that the refcount is kept + * elevated. + * + * If the driver increases the refcount when the pages are + * initially mapped into an address space then either + * invalidate_range_start() or invalidate_range_end() may + * decrease the refcount. If the refcount is decreased on + * invalidate_range_start() then the VM can free pages as page + * table entries are removed. If the refcount is only + * droppped on invalidate_range_end() then the driver itself + * will drop the last refcount but it must take care to flush + * any secondary tlb before doing the final free on the + * page. Pages will no longer be referenced by the linux + * address space but may still be referenced by sptes until + * the last refcount is dropped. + */ + void (*invalidate_range_start)(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long start, unsigned long end); + void (*invalidate_range_end)(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long start, unsigned long end); +}; + +/* + * The notifier chains are protected by mmap_sem and/or the reverse map + * semaphores. Notifier chains are only changed when all reverse maps and + * the mmap_sem locks are taken. + * + * Therefore notifier chains can only be traversed when either + * + * 1. mmap_sem is held. + * 2. One of the reverse map locks is held (i_mmap_lock or anon_vma->lock). + * 3. No other concurrent thread can access the list (release) + */ +struct mmu_notifier { + struct hlist_node hlist; + const struct mmu_notifier_ops *ops; +}; + +static inline int mm_has_notifiers(struct mm_struct *mm) +{ + return unlikely(mm->mmu_notifier_mm); +} + +extern int mmu_notifier_register(struct mmu_notifier *mn, + struct mm_struct *mm); +extern int __mmu_notifier_register(struct mmu_notifier *mn, + struct mm_struct *mm); +extern void mmu_notifier_unregister(struct mmu_notifier *mn, + struct mm_struct *mm); +extern void __mmu_notifier_mm_destroy(struct mm_struct *mm); +extern void __mmu_notifier_release(struct mm_struct *mm); +extern int __mmu_notifier_clear_flush_young(struct mm_struct *mm, + unsigned long address); +extern void __mmu_notifier_invalidate_page(struct mm_struct *mm, + unsigned long address); +extern void __mmu_notifier_invalidate_range_start(struct mm_struct *mm, + unsigned long start, unsigned long end); +extern void __mmu_notifier_invalidate_range_end(struct mm_struct *mm, + unsigned long start, unsigned long end); + +static inline void mmu_notifier_release(struct mm_struct *mm) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_release(mm); +} + +static inline int mmu_notifier_clear_flush_young(struct mm_struct *mm, + unsigned long address) +{ + if (mm_has_notifiers(mm)) + return __mmu_notifier_clear_flush_young(mm, address); + return 0; +} + +static inline void mmu_notifier_invalidate_page(struct mm_struct *mm, + unsigned long address) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_invalidate_page(mm, address); +} + +static inline void mmu_notifier_invalidate_range_start(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_invalidate_range_start(mm, start, end); +} + +static inline void mmu_notifier_invalidate_range_end(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_invalidate_range_end(mm, start, end); +} + +static inline void mmu_notifier_mm_init(struct mm_struct *mm) +{ + mm->mmu_notifier_mm = NULL; +} + +static inline void mmu_notifier_mm_destroy(struct mm_struct *mm) +{ + if (mm_has_notifiers(mm)) + __mmu_notifier_mm_destroy(mm); +} + +/* + * These two macros will sometime replace ptep_clear_flush. + * ptep_clear_flush is impleemnted as macro itself, so this also is + * implemented as a macro until ptep_clear_flush will converted to an + * inline function, to diminish the risk of compilation failure. The + * invalidate_page method over time can be moved outside the PT lock + * and these two macros can be later removed. + */ +#define ptep_clear_flush_notify(__vma, __address, __ptep) \ +({ \ + pte_t __pte; \ + struct vm_area_struct *___vma = __vma; \ + unsigned long ___address = __address; \ + __pte = ptep_clear_flush(___vma, ___address, __ptep); \ + mmu_notifier_invalidate_page(___vma->vm_mm, ___address); \ + __pte; \ +}) + +#define ptep_clear_flush_young_notify(__vma, __address, __ptep) \ +({ \ + int __young; \ + struct vm_area_struct *___vma = __vma; \ + unsigned long ___address = __address; \ + __young = ptep_clear_flush_young(___vma, ___address, __ptep); \ + __young |= mmu_notifier_clear_flush_young(___vma->vm_mm, \ + ___address); \ + __young; \ +}) + +#else /* CONFIG_MMU_NOTIFIER */ + +static inline void mmu_notifier_release(struct mm_struct *mm) +{ +} + +static inline int mmu_notifier_clear_flush_young(struct mm_struct *mm, + unsigned long address) +{ + return 0; +} + +static inline void mmu_notifier_invalidate_page(struct mm_struct *mm, + unsigned long address) +{ +} + +static inline void mmu_notifier_invalidate_range_start(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ +} + +static inline void mmu_notifier_invalidate_range_end(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ +} + +static inline void mmu_notifier_mm_init(struct mm_struct *mm) +{ +} + +static inline void mmu_notifier_mm_destroy(struct mm_struct *mm) +{ +} + +#define ptep_clear_flush_young_notify ptep_clear_flush_young +#define ptep_clear_flush_notify ptep_clear_flush + +#endif /* CONFIG_MMU_NOTIFIER */ + +#endif /* _LINUX_MMU_NOTIFIER_H */ diff --git a/kernel/fork.c b/kernel/fork.c index 8214ba7c8bb1..7ce2ebe84796 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -414,6 +415,7 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p) if (likely(!mm_alloc_pgd(mm))) { mm->def_flags = 0; + mmu_notifier_mm_init(mm); return mm; } @@ -446,6 +448,7 @@ void __mmdrop(struct mm_struct *mm) BUG_ON(mm == &init_mm); mm_free_pgd(mm); destroy_context(mm); + mmu_notifier_mm_destroy(mm); free_mm(mm); } EXPORT_SYMBOL_GPL(__mmdrop); diff --git a/mm/Kconfig b/mm/Kconfig index efee5d379df4..446c6588c753 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -208,3 +208,6 @@ config NR_QUICK config VIRT_TO_BUS def_bool y depends on !ARCH_NO_VIRT_TO_BUS + +config MMU_NOTIFIER + bool diff --git a/mm/Makefile b/mm/Makefile index 06ca2381fef1..da4ccf015aea 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SHMEM) += shmem.o obj-$(CONFIG_TMPFS_POSIX_ACL) += shmem_acl.o obj-$(CONFIG_TINY_SHMEM) += tiny-shmem.o obj-$(CONFIG_SLOB) += slob.o +obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o obj-$(CONFIG_SLAB) += slab.o obj-$(CONFIG_SLUB) += slub.o obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 98a3f31ccd6a..380ab402d711 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -188,7 +189,7 @@ __xip_unmap (struct address_space * mapping, if (pte) { /* Nuke the page table entry. */ flush_cache_page(vma, address, pte_pfn(*pte)); - pteval = ptep_clear_flush(vma, address, pte); + pteval = ptep_clear_flush_notify(vma, address, pte); page_remove_rmap(page, vma); dec_mm_counter(mm, file_rss); BUG_ON(pte_dirty(pteval)); diff --git a/mm/fremap.c b/mm/fremap.c index 07a9c82ce1a3..7881638e4a12 100644 --- a/mm/fremap.c +++ b/mm/fremap.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -214,7 +215,9 @@ asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size, spin_unlock(&mapping->i_mmap_lock); } + mmu_notifier_invalidate_range_start(mm, start, start + size); err = populate_range(mm, vma, start, size, pgoff); + mmu_notifier_invalidate_range_end(mm, start, start + size); if (!err && !(flags & MAP_NONBLOCK)) { if (unlikely(has_write_lock)) { downgrade_write(&mm->mmap_sem); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 3be79dc18c5c..80eb0d31d0d3 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1672,6 +1673,7 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, BUG_ON(start & ~huge_page_mask(h)); BUG_ON(end & ~huge_page_mask(h)); + mmu_notifier_invalidate_range_start(mm, start, end); spin_lock(&mm->page_table_lock); for (address = start; address < end; address += sz) { ptep = huge_pte_offset(mm, address); @@ -1713,6 +1715,7 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, } spin_unlock(&mm->page_table_lock); flush_tlb_range(vma, start, end); + mmu_notifier_invalidate_range_end(mm, start, end); list_for_each_entry_safe(page, tmp, &page_list, lru) { list_del(&page->lru); put_page(page); diff --git a/mm/memory.c b/mm/memory.c index a8ca04faaea6..67f0ab9077d9 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -652,6 +653,7 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm, unsigned long next; unsigned long addr = vma->vm_start; unsigned long end = vma->vm_end; + int ret; /* * Don't copy ptes where a page fault will fill them correctly. @@ -667,17 +669,33 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm, if (is_vm_hugetlb_page(vma)) return copy_hugetlb_page_range(dst_mm, src_mm, vma); + /* + * We need to invalidate the secondary MMU mappings only when + * there could be a permission downgrade on the ptes of the + * parent mm. And a permission downgrade will only happen if + * is_cow_mapping() returns true. + */ + if (is_cow_mapping(vma->vm_flags)) + mmu_notifier_invalidate_range_start(src_mm, addr, end); + + ret = 0; dst_pgd = pgd_offset(dst_mm, addr); src_pgd = pgd_offset(src_mm, addr); do { next = pgd_addr_end(addr, end); if (pgd_none_or_clear_bad(src_pgd)) continue; - if (copy_pud_range(dst_mm, src_mm, dst_pgd, src_pgd, - vma, addr, next)) - return -ENOMEM; + if (unlikely(copy_pud_range(dst_mm, src_mm, dst_pgd, src_pgd, + vma, addr, next))) { + ret = -ENOMEM; + break; + } } while (dst_pgd++, src_pgd++, addr = next, addr != end); - return 0; + + if (is_cow_mapping(vma->vm_flags)) + mmu_notifier_invalidate_range_end(src_mm, + vma->vm_start, end); + return ret; } static unsigned long zap_pte_range(struct mmu_gather *tlb, @@ -881,7 +899,9 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, unsigned long start = start_addr; spinlock_t *i_mmap_lock = details? details->i_mmap_lock: NULL; int fullmm = (*tlbp)->fullmm; + struct mm_struct *mm = vma->vm_mm; + mmu_notifier_invalidate_range_start(mm, start_addr, end_addr); for ( ; vma && vma->vm_start < end_addr; vma = vma->vm_next) { unsigned long end; @@ -946,6 +966,7 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, } } out: + mmu_notifier_invalidate_range_end(mm, start_addr, end_addr); return start; /* which is now the end (or restart) address */ } @@ -1616,10 +1637,11 @@ int apply_to_page_range(struct mm_struct *mm, unsigned long addr, { pgd_t *pgd; unsigned long next; - unsigned long end = addr + size; + unsigned long start = addr, end = addr + size; int err; BUG_ON(addr >= end); + mmu_notifier_invalidate_range_start(mm, start, end); pgd = pgd_offset(mm, addr); do { next = pgd_addr_end(addr, end); @@ -1627,6 +1649,7 @@ int apply_to_page_range(struct mm_struct *mm, unsigned long addr, if (err) break; } while (pgd++, addr = next, addr != end); + mmu_notifier_invalidate_range_end(mm, start, end); return err; } EXPORT_SYMBOL_GPL(apply_to_page_range); @@ -1839,7 +1862,7 @@ gotten: * seen in the presence of one thread doing SMC and another * thread doing COW. */ - ptep_clear_flush(vma, address, page_table); + ptep_clear_flush_notify(vma, address, page_table); set_pte_at(mm, address, page_table, entry); update_mmu_cache(vma, address, entry); lru_cache_add_active(new_page); diff --git a/mm/mmap.c b/mm/mmap.c index e5f9cb83d6d4..245c3d69067b 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -2061,6 +2062,7 @@ void exit_mmap(struct mm_struct *mm) /* mm's last user has gone, and its about to be pulled down */ arch_exit_mmap(mm); + mmu_notifier_release(mm); lru_add_drain(); flush_cache_mm(mm); diff --git a/mm/mmu_notifier.c b/mm/mmu_notifier.c new file mode 100644 index 000000000000..5f4ef0250bee --- /dev/null +++ b/mm/mmu_notifier.c @@ -0,0 +1,277 @@ +/* + * linux/mm/mmu_notifier.c + * + * Copyright (C) 2008 Qumranet, Inc. + * Copyright (C) 2008 SGI + * Christoph Lameter + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * This function can't run concurrently against mmu_notifier_register + * because mm->mm_users > 0 during mmu_notifier_register and exit_mmap + * runs with mm_users == 0. Other tasks may still invoke mmu notifiers + * in parallel despite there being no task using this mm any more, + * through the vmas outside of the exit_mmap context, such as with + * vmtruncate. This serializes against mmu_notifier_unregister with + * the mmu_notifier_mm->lock in addition to RCU and it serializes + * against the other mmu notifiers with RCU. struct mmu_notifier_mm + * can't go away from under us as exit_mmap holds an mm_count pin + * itself. + */ +void __mmu_notifier_release(struct mm_struct *mm) +{ + struct mmu_notifier *mn; + + spin_lock(&mm->mmu_notifier_mm->lock); + while (unlikely(!hlist_empty(&mm->mmu_notifier_mm->list))) { + mn = hlist_entry(mm->mmu_notifier_mm->list.first, + struct mmu_notifier, + hlist); + /* + * We arrived before mmu_notifier_unregister so + * mmu_notifier_unregister will do nothing other than + * to wait ->release to finish and + * mmu_notifier_unregister to return. + */ + hlist_del_init_rcu(&mn->hlist); + /* + * RCU here will block mmu_notifier_unregister until + * ->release returns. + */ + rcu_read_lock(); + spin_unlock(&mm->mmu_notifier_mm->lock); + /* + * if ->release runs before mmu_notifier_unregister it + * must be handled as it's the only way for the driver + * to flush all existing sptes and stop the driver + * from establishing any more sptes before all the + * pages in the mm are freed. + */ + if (mn->ops->release) + mn->ops->release(mn, mm); + rcu_read_unlock(); + spin_lock(&mm->mmu_notifier_mm->lock); + } + spin_unlock(&mm->mmu_notifier_mm->lock); + + /* + * synchronize_rcu here prevents mmu_notifier_release to + * return to exit_mmap (which would proceed freeing all pages + * in the mm) until the ->release method returns, if it was + * invoked by mmu_notifier_unregister. + * + * The mmu_notifier_mm can't go away from under us because one + * mm_count is hold by exit_mmap. + */ + synchronize_rcu(); +} + +/* + * If no young bitflag is supported by the hardware, ->clear_flush_young can + * unmap the address and return 1 or 0 depending if the mapping previously + * existed or not. + */ +int __mmu_notifier_clear_flush_young(struct mm_struct *mm, + unsigned long address) +{ + struct mmu_notifier *mn; + struct hlist_node *n; + int young = 0; + + rcu_read_lock(); + hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist) { + if (mn->ops->clear_flush_young) + young |= mn->ops->clear_flush_young(mn, mm, address); + } + rcu_read_unlock(); + + return young; +} + +void __mmu_notifier_invalidate_page(struct mm_struct *mm, + unsigned long address) +{ + struct mmu_notifier *mn; + struct hlist_node *n; + + rcu_read_lock(); + hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist) { + if (mn->ops->invalidate_page) + mn->ops->invalidate_page(mn, mm, address); + } + rcu_read_unlock(); +} + +void __mmu_notifier_invalidate_range_start(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + struct mmu_notifier *mn; + struct hlist_node *n; + + rcu_read_lock(); + hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist) { + if (mn->ops->invalidate_range_start) + mn->ops->invalidate_range_start(mn, mm, start, end); + } + rcu_read_unlock(); +} + +void __mmu_notifier_invalidate_range_end(struct mm_struct *mm, + unsigned long start, unsigned long end) +{ + struct mmu_notifier *mn; + struct hlist_node *n; + + rcu_read_lock(); + hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist) { + if (mn->ops->invalidate_range_end) + mn->ops->invalidate_range_end(mn, mm, start, end); + } + rcu_read_unlock(); +} + +static int do_mmu_notifier_register(struct mmu_notifier *mn, + struct mm_struct *mm, + int take_mmap_sem) +{ + struct mmu_notifier_mm *mmu_notifier_mm; + int ret; + + BUG_ON(atomic_read(&mm->mm_users) <= 0); + + ret = -ENOMEM; + mmu_notifier_mm = kmalloc(sizeof(struct mmu_notifier_mm), GFP_KERNEL); + if (unlikely(!mmu_notifier_mm)) + goto out; + + if (take_mmap_sem) + down_write(&mm->mmap_sem); + ret = mm_take_all_locks(mm); + if (unlikely(ret)) + goto out_cleanup; + + if (!mm_has_notifiers(mm)) { + INIT_HLIST_HEAD(&mmu_notifier_mm->list); + spin_lock_init(&mmu_notifier_mm->lock); + mm->mmu_notifier_mm = mmu_notifier_mm; + mmu_notifier_mm = NULL; + } + atomic_inc(&mm->mm_count); + + /* + * Serialize the update against mmu_notifier_unregister. A + * side note: mmu_notifier_release can't run concurrently with + * us because we hold the mm_users pin (either implicitly as + * current->mm or explicitly with get_task_mm() or similar). + * We can't race against any other mmu notifier method either + * thanks to mm_take_all_locks(). + */ + spin_lock(&mm->mmu_notifier_mm->lock); + hlist_add_head(&mn->hlist, &mm->mmu_notifier_mm->list); + spin_unlock(&mm->mmu_notifier_mm->lock); + + mm_drop_all_locks(mm); +out_cleanup: + if (take_mmap_sem) + up_write(&mm->mmap_sem); + /* kfree() does nothing if mmu_notifier_mm is NULL */ + kfree(mmu_notifier_mm); +out: + BUG_ON(atomic_read(&mm->mm_users) <= 0); + return ret; +} + +/* + * Must not hold mmap_sem nor any other VM related lock when calling + * this registration function. Must also ensure mm_users can't go down + * to zero while this runs to avoid races with mmu_notifier_release, + * so mm has to be current->mm or the mm should be pinned safely such + * as with get_task_mm(). If the mm is not current->mm, the mm_users + * pin should be released by calling mmput after mmu_notifier_register + * returns. mmu_notifier_unregister must be always called to + * unregister the notifier. mm_count is automatically pinned to allow + * mmu_notifier_unregister to safely run at any time later, before or + * after exit_mmap. ->release will always be called before exit_mmap + * frees the pages. + */ +int mmu_notifier_register(struct mmu_notifier *mn, struct mm_struct *mm) +{ + return do_mmu_notifier_register(mn, mm, 1); +} +EXPORT_SYMBOL_GPL(mmu_notifier_register); + +/* + * Same as mmu_notifier_register but here the caller must hold the + * mmap_sem in write mode. + */ +int __mmu_notifier_register(struct mmu_notifier *mn, struct mm_struct *mm) +{ + return do_mmu_notifier_register(mn, mm, 0); +} +EXPORT_SYMBOL_GPL(__mmu_notifier_register); + +/* this is called after the last mmu_notifier_unregister() returned */ +void __mmu_notifier_mm_destroy(struct mm_struct *mm) +{ + BUG_ON(!hlist_empty(&mm->mmu_notifier_mm->list)); + kfree(mm->mmu_notifier_mm); + mm->mmu_notifier_mm = LIST_POISON1; /* debug */ +} + +/* + * This releases the mm_count pin automatically and frees the mm + * structure if it was the last user of it. It serializes against + * running mmu notifiers with RCU and against mmu_notifier_unregister + * with the unregister lock + RCU. All sptes must be dropped before + * calling mmu_notifier_unregister. ->release or any other notifier + * method may be invoked concurrently with mmu_notifier_unregister, + * and only after mmu_notifier_unregister returned we're guaranteed + * that ->release or any other method can't run anymore. + */ +void mmu_notifier_unregister(struct mmu_notifier *mn, struct mm_struct *mm) +{ + BUG_ON(atomic_read(&mm->mm_count) <= 0); + + spin_lock(&mm->mmu_notifier_mm->lock); + if (!hlist_unhashed(&mn->hlist)) { + hlist_del_rcu(&mn->hlist); + + /* + * RCU here will force exit_mmap to wait ->release to finish + * before freeing the pages. + */ + rcu_read_lock(); + spin_unlock(&mm->mmu_notifier_mm->lock); + /* + * exit_mmap will block in mmu_notifier_release to + * guarantee ->release is called before freeing the + * pages. + */ + if (mn->ops->release) + mn->ops->release(mn, mm); + rcu_read_unlock(); + } else + spin_unlock(&mm->mmu_notifier_mm->lock); + + /* + * Wait any running method to finish, of course including + * ->release if it was run by mmu_notifier_relase instead of us. + */ + synchronize_rcu(); + + BUG_ON(atomic_read(&mm->mm_count) <= 0); + + mmdrop(mm); +} +EXPORT_SYMBOL_GPL(mmu_notifier_unregister); diff --git a/mm/mprotect.c b/mm/mprotect.c index abd645a3b0a0..fded06f923f4 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -203,10 +204,12 @@ success: dirty_accountable = 1; } + mmu_notifier_invalidate_range_start(mm, start, end); if (is_vm_hugetlb_page(vma)) hugetlb_change_protection(vma, start, end, vma->vm_page_prot); else change_protection(vma, start, end, vma->vm_page_prot, dirty_accountable); + mmu_notifier_invalidate_range_end(mm, start, end); vm_stat_account(mm, oldflags, vma->vm_file, -nrpages); vm_stat_account(mm, newflags, vma->vm_file, nrpages); return 0; diff --git a/mm/mremap.c b/mm/mremap.c index 08e3c7f2bd15..1a7743923c8c 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -74,7 +75,11 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd, struct mm_struct *mm = vma->vm_mm; pte_t *old_pte, *new_pte, pte; spinlock_t *old_ptl, *new_ptl; + unsigned long old_start; + old_start = old_addr; + mmu_notifier_invalidate_range_start(vma->vm_mm, + old_start, old_end); if (vma->vm_file) { /* * Subtle point from Rajesh Venkatasubramanian: before @@ -116,6 +121,7 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd, pte_unmap_unlock(old_pte - 1, old_ptl); if (mapping) spin_unlock(&mapping->i_mmap_lock); + mmu_notifier_invalidate_range_end(vma->vm_mm, old_start, old_end); } #define LATENCY_LIMIT (64 * PAGE_SIZE) diff --git a/mm/rmap.c b/mm/rmap.c index 39ae5a9bf382..99bc3f9cd796 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -49,6 +49,7 @@ #include #include #include +#include #include @@ -287,7 +288,7 @@ static int page_referenced_one(struct page *page, if (vma->vm_flags & VM_LOCKED) { referenced++; *mapcount = 1; /* break early from loop */ - } else if (ptep_clear_flush_young(vma, address, pte)) + } else if (ptep_clear_flush_young_notify(vma, address, pte)) referenced++; /* Pretend the page is referenced if the task has the @@ -457,7 +458,7 @@ static int page_mkclean_one(struct page *page, struct vm_area_struct *vma) pte_t entry; flush_cache_page(vma, address, pte_pfn(*pte)); - entry = ptep_clear_flush(vma, address, pte); + entry = ptep_clear_flush_notify(vma, address, pte); entry = pte_wrprotect(entry); entry = pte_mkclean(entry); set_pte_at(mm, address, pte, entry); @@ -705,14 +706,14 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, * skipped over this mm) then we should reactivate it. */ if (!migration && ((vma->vm_flags & VM_LOCKED) || - (ptep_clear_flush_young(vma, address, pte)))) { + (ptep_clear_flush_young_notify(vma, address, pte)))) { ret = SWAP_FAIL; goto out_unmap; } /* Nuke the page table entry. */ flush_cache_page(vma, address, page_to_pfn(page)); - pteval = ptep_clear_flush(vma, address, pte); + pteval = ptep_clear_flush_notify(vma, address, pte); /* Move the dirty bit to the physical page now the pte is gone. */ if (pte_dirty(pteval)) @@ -837,12 +838,12 @@ static void try_to_unmap_cluster(unsigned long cursor, page = vm_normal_page(vma, address, *pte); BUG_ON(!page || PageAnon(page)); - if (ptep_clear_flush_young(vma, address, pte)) + if (ptep_clear_flush_young_notify(vma, address, pte)) continue; /* Nuke the page table entry. */ flush_cache_page(vma, address, pte_pfn(*pte)); - pteval = ptep_clear_flush(vma, address, pte); + pteval = ptep_clear_flush_notify(vma, address, pte); /* If nonlinear, store the file page offset in the pte. */ if (page->index != linear_page_index(vma, address)) -- cgit From 8ab22b9abb5c55413802e4adc9aa6223324547c3 Mon Sep 17 00:00:00 2001 From: Hisashi Hifumi Date: Mon, 28 Jul 2008 15:46:36 -0700 Subject: vfs: pagecache usage optimization for pagesize!=blocksize When we read some part of a file through pagecache, if there is a pagecache of corresponding index but this page is not uptodate, read IO is issued and this page will be uptodate. I think this is good for pagesize == blocksize environment but there is room for improvement on pagesize != blocksize environment. Because in this case a page can have multiple buffers and even if a page is not uptodate, some buffers can be uptodate. So I suggest that when all buffers which correspond to a part of a file that we want to read are uptodate, use this pagecache and copy data from this pagecache to user buffer even if a page is not uptodate. This can reduce read IO and improve system throughput. I wrote a benchmark program and got result number with this program. This benchmark do: 1: mount and open a test file. 2: create a 512MB file. 3: close a file and umount. 4: mount and again open a test file. 5: pwrite randomly 300000 times on a test file. offset is aligned by IO size(1024bytes). 6: measure time of preading randomly 100000 times on a test file. The result was: 2.6.26 330 sec 2.6.26-patched 226 sec Arch:i386 Filesystem:ext3 Blocksize:1024 bytes Memory: 1GB On ext3/4, a file is written through buffer/block. So random read/write mixed workloads or random read after random write workloads are optimized with this patch under pagesize != blocksize environment. This test result showed this. The benchmark program is as follows: #include #include #include #include #include #include #include #include #include #define LEN 1024 #define LOOP 1024*512 /* 512MB */ main(void) { unsigned long i, offset, filesize; int fd; char buf[LEN]; time_t t1, t2; if (mount("/dev/sda1", "/root/test1/", "ext3", 0, 0) < 0) { perror("cannot mount\n"); exit(1); } memset(buf, 0, LEN); fd = open("/root/test1/testfile", O_CREAT|O_RDWR|O_TRUNC); if (fd < 0) { perror("cannot open file\n"); exit(1); } for (i = 0; i < LOOP; i++) write(fd, buf, LEN); close(fd); if (umount("/root/test1/") < 0) { perror("cannot umount\n"); exit(1); } if (mount("/dev/sda1", "/root/test1/", "ext3", 0, 0) < 0) { perror("cannot mount\n"); exit(1); } fd = open("/root/test1/testfile", O_RDWR); if (fd < 0) { perror("cannot open file\n"); exit(1); } filesize = LEN * LOOP; for (i = 0; i < 300000; i++){ offset = (random() % filesize) & (~(LEN - 1)); pwrite(fd, buf, LEN, offset); } printf("start test\n"); time(&t1); for (i = 0; i < 100000; i++){ offset = (random() % filesize) & (~(LEN - 1)); pread(fd, buf, LEN, offset); } time(&t2); printf("%ld sec\n", t2-t1); close(fd); if (umount("/root/test1/") < 0) { perror("cannot umount\n"); exit(1); } } Signed-off-by: Hisashi Hifumi Cc: Nick Piggin Cc: Christoph Hellwig Cc: Jan Kara Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/buffer.c | 46 +++++++++++++++++++++++ fs/ext2/inode.c | 1 + fs/ext3/inode.c | 67 +++++++++++++++++---------------- fs/ext4/inode.c | 92 +++++++++++++++++++++++---------------------- include/linux/buffer_head.h | 2 + include/linux/fs.h | 44 +++++++++++----------- mm/filemap.c | 14 ++++++- 7 files changed, 167 insertions(+), 99 deletions(-) (limited to 'include/linux') diff --git a/fs/buffer.c b/fs/buffer.c index f95805019639..ca12a6bb82b1 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2095,6 +2095,52 @@ int generic_write_end(struct file *file, struct address_space *mapping, } EXPORT_SYMBOL(generic_write_end); +/* + * block_is_partially_uptodate checks whether buffers within a page are + * uptodate or not. + * + * Returns true if all buffers which correspond to a file portion + * we want to read are uptodate. + */ +int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc, + unsigned long from) +{ + struct inode *inode = page->mapping->host; + unsigned block_start, block_end, blocksize; + unsigned to; + struct buffer_head *bh, *head; + int ret = 1; + + if (!page_has_buffers(page)) + return 0; + + blocksize = 1 << inode->i_blkbits; + to = min_t(unsigned, PAGE_CACHE_SIZE - from, desc->count); + to = from + to; + if (from < blocksize && to > PAGE_CACHE_SIZE - blocksize) + return 0; + + head = page_buffers(page); + bh = head; + block_start = 0; + do { + block_end = block_start + blocksize; + if (block_end > from && block_start < to) { + if (!buffer_uptodate(bh)) { + ret = 0; + break; + } + if (block_end >= to) + break; + } + block_start = block_end; + bh = bh->b_this_page; + } while (bh != head); + + return ret; +} +EXPORT_SYMBOL(block_is_partially_uptodate); + /* * Generic "read page" function for block devices that have the normal * get_block functionality. This is most of the block device filesystems. diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 384fc0d1dd74..991d6dfeb51f 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -791,6 +791,7 @@ const struct address_space_operations ext2_aops = { .direct_IO = ext2_direct_IO, .writepages = ext2_writepages, .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; const struct address_space_operations ext2_aops_xip = { diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 3bf07d70b914..507d8689b111 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1767,44 +1767,47 @@ static int ext3_journalled_set_page_dirty(struct page *page) } static const struct address_space_operations ext3_ordered_aops = { - .readpage = ext3_readpage, - .readpages = ext3_readpages, - .writepage = ext3_ordered_writepage, - .sync_page = block_sync_page, - .write_begin = ext3_write_begin, - .write_end = ext3_ordered_write_end, - .bmap = ext3_bmap, - .invalidatepage = ext3_invalidatepage, - .releasepage = ext3_releasepage, - .direct_IO = ext3_direct_IO, - .migratepage = buffer_migrate_page, + .readpage = ext3_readpage, + .readpages = ext3_readpages, + .writepage = ext3_ordered_writepage, + .sync_page = block_sync_page, + .write_begin = ext3_write_begin, + .write_end = ext3_ordered_write_end, + .bmap = ext3_bmap, + .invalidatepage = ext3_invalidatepage, + .releasepage = ext3_releasepage, + .direct_IO = ext3_direct_IO, + .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; static const struct address_space_operations ext3_writeback_aops = { - .readpage = ext3_readpage, - .readpages = ext3_readpages, - .writepage = ext3_writeback_writepage, - .sync_page = block_sync_page, - .write_begin = ext3_write_begin, - .write_end = ext3_writeback_write_end, - .bmap = ext3_bmap, - .invalidatepage = ext3_invalidatepage, - .releasepage = ext3_releasepage, - .direct_IO = ext3_direct_IO, - .migratepage = buffer_migrate_page, + .readpage = ext3_readpage, + .readpages = ext3_readpages, + .writepage = ext3_writeback_writepage, + .sync_page = block_sync_page, + .write_begin = ext3_write_begin, + .write_end = ext3_writeback_write_end, + .bmap = ext3_bmap, + .invalidatepage = ext3_invalidatepage, + .releasepage = ext3_releasepage, + .direct_IO = ext3_direct_IO, + .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; static const struct address_space_operations ext3_journalled_aops = { - .readpage = ext3_readpage, - .readpages = ext3_readpages, - .writepage = ext3_journalled_writepage, - .sync_page = block_sync_page, - .write_begin = ext3_write_begin, - .write_end = ext3_journalled_write_end, - .set_page_dirty = ext3_journalled_set_page_dirty, - .bmap = ext3_bmap, - .invalidatepage = ext3_invalidatepage, - .releasepage = ext3_releasepage, + .readpage = ext3_readpage, + .readpages = ext3_readpages, + .writepage = ext3_journalled_writepage, + .sync_page = block_sync_page, + .write_begin = ext3_write_begin, + .write_end = ext3_journalled_write_end, + .set_page_dirty = ext3_journalled_set_page_dirty, + .bmap = ext3_bmap, + .invalidatepage = ext3_invalidatepage, + .releasepage = ext3_releasepage, + .is_partially_uptodate = block_is_partially_uptodate, }; void ext3_set_aops(struct inode *inode) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8ca2763df091..9843b046c235 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2806,59 +2806,63 @@ static int ext4_journalled_set_page_dirty(struct page *page) } static const struct address_space_operations ext4_ordered_aops = { - .readpage = ext4_readpage, - .readpages = ext4_readpages, - .writepage = ext4_normal_writepage, - .sync_page = block_sync_page, - .write_begin = ext4_write_begin, - .write_end = ext4_ordered_write_end, - .bmap = ext4_bmap, - .invalidatepage = ext4_invalidatepage, - .releasepage = ext4_releasepage, - .direct_IO = ext4_direct_IO, - .migratepage = buffer_migrate_page, + .readpage = ext4_readpage, + .readpages = ext4_readpages, + .writepage = ext4_normal_writepage, + .sync_page = block_sync_page, + .write_begin = ext4_write_begin, + .write_end = ext4_ordered_write_end, + .bmap = ext4_bmap, + .invalidatepage = ext4_invalidatepage, + .releasepage = ext4_releasepage, + .direct_IO = ext4_direct_IO, + .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; static const struct address_space_operations ext4_writeback_aops = { - .readpage = ext4_readpage, - .readpages = ext4_readpages, - .writepage = ext4_normal_writepage, - .sync_page = block_sync_page, - .write_begin = ext4_write_begin, - .write_end = ext4_writeback_write_end, - .bmap = ext4_bmap, - .invalidatepage = ext4_invalidatepage, - .releasepage = ext4_releasepage, - .direct_IO = ext4_direct_IO, - .migratepage = buffer_migrate_page, + .readpage = ext4_readpage, + .readpages = ext4_readpages, + .writepage = ext4_normal_writepage, + .sync_page = block_sync_page, + .write_begin = ext4_write_begin, + .write_end = ext4_writeback_write_end, + .bmap = ext4_bmap, + .invalidatepage = ext4_invalidatepage, + .releasepage = ext4_releasepage, + .direct_IO = ext4_direct_IO, + .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; static const struct address_space_operations ext4_journalled_aops = { - .readpage = ext4_readpage, - .readpages = ext4_readpages, - .writepage = ext4_journalled_writepage, - .sync_page = block_sync_page, - .write_begin = ext4_write_begin, - .write_end = ext4_journalled_write_end, - .set_page_dirty = ext4_journalled_set_page_dirty, - .bmap = ext4_bmap, - .invalidatepage = ext4_invalidatepage, - .releasepage = ext4_releasepage, + .readpage = ext4_readpage, + .readpages = ext4_readpages, + .writepage = ext4_journalled_writepage, + .sync_page = block_sync_page, + .write_begin = ext4_write_begin, + .write_end = ext4_journalled_write_end, + .set_page_dirty = ext4_journalled_set_page_dirty, + .bmap = ext4_bmap, + .invalidatepage = ext4_invalidatepage, + .releasepage = ext4_releasepage, + .is_partially_uptodate = block_is_partially_uptodate, }; static const struct address_space_operations ext4_da_aops = { - .readpage = ext4_readpage, - .readpages = ext4_readpages, - .writepage = ext4_da_writepage, - .writepages = ext4_da_writepages, - .sync_page = block_sync_page, - .write_begin = ext4_da_write_begin, - .write_end = ext4_da_write_end, - .bmap = ext4_bmap, - .invalidatepage = ext4_da_invalidatepage, - .releasepage = ext4_releasepage, - .direct_IO = ext4_direct_IO, - .migratepage = buffer_migrate_page, + .readpage = ext4_readpage, + .readpages = ext4_readpages, + .writepage = ext4_da_writepage, + .writepages = ext4_da_writepages, + .sync_page = block_sync_page, + .write_begin = ext4_da_write_begin, + .write_end = ext4_da_write_end, + .bmap = ext4_bmap, + .invalidatepage = ext4_da_invalidatepage, + .releasepage = ext4_releasepage, + .direct_IO = ext4_direct_IO, + .migratepage = buffer_migrate_page, + .is_partially_uptodate = block_is_partially_uptodate, }; void ext4_set_aops(struct inode *inode) diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 82aa36c53ea7..50cfe8ceb478 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -205,6 +205,8 @@ void block_invalidatepage(struct page *page, unsigned long offset); int block_write_full_page(struct page *page, get_block_t *get_block, struct writeback_control *wbc); int block_read_full_page(struct page*, get_block_t*); +int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc, + unsigned long from); int block_write_begin(struct file *, struct address_space *, loff_t, unsigned, unsigned, struct page **, void **, get_block_t*); diff --git a/include/linux/fs.h b/include/linux/fs.h index 8252b045e624..580b513668fe 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -443,6 +443,27 @@ static inline size_t iov_iter_count(struct iov_iter *i) return i->count; } +/* + * "descriptor" for what we're up to with a read. + * This allows us to use the same read code yet + * have multiple different users of the data that + * we read from a file. + * + * The simplest case just copies the data to user + * mode. + */ +typedef struct { + size_t written; + size_t count; + union { + char __user *buf; + void *data; + } arg; + int error; +} read_descriptor_t; + +typedef int (*read_actor_t)(read_descriptor_t *, struct page *, + unsigned long, unsigned long); struct address_space_operations { int (*writepage)(struct page *page, struct writeback_control *wbc); @@ -484,6 +505,8 @@ struct address_space_operations { int (*migratepage) (struct address_space *, struct page *, struct page *); int (*launder_page) (struct page *); + int (*is_partially_uptodate) (struct page *, read_descriptor_t *, + unsigned long); }; /* @@ -1198,27 +1221,6 @@ struct block_device_operations { struct module *owner; }; -/* - * "descriptor" for what we're up to with a read. - * This allows us to use the same read code yet - * have multiple different users of the data that - * we read from a file. - * - * The simplest case just copies the data to user - * mode. - */ -typedef struct { - size_t written; - size_t count; - union { - char __user * buf; - void *data; - } arg; - int error; -} read_descriptor_t; - -typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, unsigned long); - /* These macros are for out of kernel modules to test that * the kernel supports the unlocked_ioctl and compat_ioctl * fields in struct file_operations. */ diff --git a/mm/filemap.c b/mm/filemap.c index 5de7633e1dbe..42bbc6909ba4 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1023,8 +1023,17 @@ find_page: ra, filp, page, index, last_index - index); } - if (!PageUptodate(page)) - goto page_not_up_to_date; + if (!PageUptodate(page)) { + if (inode->i_blkbits == PAGE_CACHE_SHIFT || + !mapping->a_ops->is_partially_uptodate) + goto page_not_up_to_date; + if (TestSetPageLocked(page)) + goto page_not_up_to_date; + if (!mapping->a_ops->is_partially_uptodate(page, + desc, offset)) + goto page_not_up_to_date_locked; + unlock_page(page); + } page_ok: /* * i_size must be checked after we know the page is Uptodate. @@ -1094,6 +1103,7 @@ page_not_up_to_date: if (lock_page_killable(page)) goto readpage_eio; +page_not_up_to_date_locked: /* Did it get truncated before we got the lock? */ if (!page->mapping) { unlock_page(page); -- cgit From 424f525a1241351da947fb48a938128ddd774511 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 29 Jul 2008 01:30:26 +0200 Subject: mfd: accept pure device as a parent, not only platform_device Signed-off-by: Dmitry Baryshkov Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 14 +++++++------- drivers/mfd/tc6393xb.c | 4 ++-- include/linux/mfd/core.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index ad4e4d16a36a..9c9c126ed334 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -15,7 +15,7 @@ #include #include -static int mfd_add_device(struct platform_device *parent, +static int mfd_add_device(struct device *parent, int id, const struct mfd_cell *cell, struct resource *mem_base, int irq_base) @@ -25,11 +25,11 @@ static int mfd_add_device(struct platform_device *parent, int ret = -ENOMEM; int r; - pdev = platform_device_alloc(cell->name, parent->id); + pdev = platform_device_alloc(cell->name, id); if (!pdev) goto fail_alloc; - pdev->dev.parent = &parent->dev; + pdev->dev.parent = parent; ret = platform_device_add_data(pdev, cell->platform_data, cell->data_size); @@ -75,7 +75,7 @@ fail_alloc: return ret; } -int mfd_add_devices(struct platform_device *parent, +int mfd_add_devices(struct device *parent, int id, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base) @@ -84,7 +84,7 @@ int mfd_add_devices(struct platform_device *parent, int ret = 0; for (i = 0; i < n_devs; i++) { - ret = mfd_add_device(parent, cells + i, mem_base, irq_base); + ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base); if (ret) break; } @@ -102,9 +102,9 @@ static int mfd_remove_devices_fn(struct device *dev, void *unused) return 0; } -void mfd_remove_devices(struct platform_device *parent) +void mfd_remove_devices(struct device *parent) { - device_for_each_child(&parent->dev, NULL, mfd_remove_devices_fn); + device_for_each_child(parent, NULL, mfd_remove_devices_fn); } EXPORT_SYMBOL(mfd_remove_devices); diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 9908aaa4881a..f4fd797c1590 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -471,7 +471,7 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) tc6393xb_cells[TC6393XB_CELL_NAND].data_size = sizeof(tc6393xb_cells[TC6393XB_CELL_NAND]); - retval = mfd_add_devices(dev, + retval = mfd_add_devices(&dev->dev, dev->id, tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), iomem, tcpd->irq_base); @@ -505,7 +505,7 @@ static int __devexit tc6393xb_remove(struct platform_device *dev) struct tc6393xb *tc6393xb = platform_get_drvdata(dev); int ret; - mfd_remove_devices(dev); + mfd_remove_devices(&dev->dev); if (tc6393xb->irq) tc6393xb_detach_irq(dev); diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index ea45d4a5a2ac..49ef857cdb2d 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -45,11 +45,11 @@ struct mfd_cell { const struct resource *resources; }; -extern int mfd_add_devices(struct platform_device *parent, +extern int mfd_add_devices(struct device *parent, int id, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base); -extern void mfd_remove_devices(struct platform_device *parent); +extern void mfd_remove_devices(struct device *parent); #endif -- cgit